一、 代理模式
举例了一个场景,一个人A想追一个妹子C,可是A不认识C,而正好他的好朋友B认识C,A便托B去给C送花送礼物之类的,B就是A的代理为C服务。
大话设计模式的解释是:为其他对象提供一种代理以控制对这个对象的访问。
代理模式也是常用的java设计模式,特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,委托类负责具体的实现。
二、 Java代理实现
1、静态代理简单实现
根据上面代理模式的类图,来写一个简单的静态代理的例子。我这儿举一个比较粗糙的例子:
一个人A想追一个妹子C,可是A不认识C,而正好他的好朋友B认识C,A便托B去给C送花,B就是A的代理为C服务。
首先,我们创建一个IGift接口。这个接口就是A(被代理类),和B(代理类)的公共接口,他们都有送礼物的方法。这样,A送的礼物就可以让B来代理执行。
IGift接口
package com.yangpeng.thread.Java代理;
/**
* 代理类与委托类有同样的接口
*/
public interface IGift {
/**
* 送花
*/
void sendFlowers();
}
A类实现IGift接口。A可以具体实施送花的实现。
package com.yangpeng.thread.Java代理;
/**
* 委托类-具体的实现
*/
public class A implements IGift{
@Override
public void sendFlowers(){
System.out.println("送给C一束玫瑰花");
}
}
BProxy类,这个类也实现了IGift接口,但是还和A对象有关系。由于实现了IGift接口,同时持有一个A对象,那么B可以代理A对象执行送花给C行为。
package com.yangpeng.thread.Java代理;
/**
* 代理类,和委托类存在关联关系
* A代理类,也实现了IGift接口,保存一个A实体,这样既可以代理A产生行为
*/
public class BProxy implements IGift{
//被代理的A
public A a;
public BProxy(IGift a) {
if(a.getClass() == A.class)
this.a = (A)a;
}
//代理送花,调用被代理A送花的行为
@Override
public void sendFlowers(){
System.out.println(a.getName()+"让我送的。");
a.sendFlowers();
}
}
使用代理模式:
package com.yangpeng.thread.Java代理;
public class Demo {
public static void main(String[] args) {
//被代理对象:A先生
IGift A = new A("A先生");
//代理对象B,并将A代理给B
IGift B = new BProxy(A);
//B代理送花给C,并告诉C是A送的
B.sendFlowers();
}
}
运行结果:
总结:
这里并没有直接通过A(被代理对象)来执行送花给C的行为,而是通过B(代理对象)来代理执行了。这就是代理模式。
代理模式最主要的就是有一个公共接口(IGift),一个具体的类(A),一个代理类(BProxy),代理类持有具体类的实例,代为执行具体类实例方法。代理模式在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。就这个例子来说,加入B在帮A送花之前想要先反映一下是谁送的话,通过代理模式很轻松就能办到。
2、动态代理简单实现
定义:代理类在程序运行时创建的代理方式被成为动态代理。
优点:相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
使用:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
- 创建一个InvocationHandler对象
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler giftMyInvocationHandler = new MyInvocationHandler<IGift>(A);
package com.yangpeng.thread.Java代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler<T> implements InvocationHandler {
//invocationHandler持有的被代理对象
T target;
public MyInvocationHandler(T target){
this.target = target;
}
/**
* proxy:代表动态代理对象
* method:代表正在执行的方法
* args:代表调用目标方法时传入的实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
//代理过程中插入个性化打印
System.out.println("代理执行" +method.getName() + "方法");
Object result = method.invoke(target, args);
return result;
}
}
- 使用Proxy类的getProxyClass静态方法生成一个动态代理类BProxyClass
Class<?> bProxyClass = Proxy.getProxyClass(IGift.class.getClassLoader(),new Class<?>[]{IGift.class});
- 获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor
Constructor<?> constructor = bProxyClass.getConstructor(InvocationHandler.class);
- 通过构造器constructor来创建一个动态实例BProxy
IGift bProxy = (IGift) constructor.newInstance(new Object[]{giftMyInvocationHandler});
完整代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String[] args) throws Exception{
//1、被代理对象:A先生
IGift A = new A("A先生");
//2、创建一个InvocationHandler对象
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler giftMyInvocationHandler = new MyInvocationHandler<IGift>(A);
//3、使用Proxy类的getProxyClass静态方法生成一个动态代理类bProxyClass
Class<?> bProxyClass = Proxy.getProxyClass(IGift.class.getClassLoader(),new Class<?>[]{IGift.class});
//4、获得cbProxyClass中一个带InvocationHandler参数的构造器constructor
Constructor<?> constructor = bProxyClass.getConstructor(InvocationHandler.class);
//5、创建一个代理对象 bProxy来代理A,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
IGift bProxy = (IGift) constructor.newInstance(new Object[]{giftMyInvocationHandler});
//6、B代理执行送花
bProxy.sendFlowers();
}
}
一个动态代理对象就创建完毕,上面四个步骤可以通过Proxy类的newProxyInstances方法来简化:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String[] args) throws Exception{
IGift A = new A("A先生");
InvocationHandler giftMyInvocationHandler = new MyInvocationHandler<IGift>(A);
IGift bProxy = (IGift) Proxy.newProxyInstance(IGift.class.getClassLoader(),new Class<?>[]{IGift.class},giftMyInvocationHandler);
bProxy.sendFlowers();
}
}
3、动态代理根据源码解析原理
上面我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//TODO getProxyClass静态方法生成一个动态代理类
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//TODO 获得cbProxyClass中一个带InvocationHandler参数的构造器constructor
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//TODO·创建一个代理对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
其中,我们最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的,我们可以通过下面的方法将其打印到文件里面,一睹真容:
public class Test {
public static void main(String[] args) throws Exception{
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", IGift.class.getInterfaces());
String path = "D:\\学习\\bProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
} catch (Exception e) {
System.out.println("写文件错误");
}
}
}
对bProxy.class文件进行反编译如下:
import com.yangpeng.thread.Java代理.IGift;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements IGift
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
/**
*
*这里调用代理对象的sendFlowers方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void sendFlowers()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
//看看这儿静态块儿里面有什么,是不是找到了sendFlowers方法。请记住sendFlowers通过反射得到的名字m3,其他的先不管
m3 = Class.forName("com.yangpeng.thread.Java代理.IGift").getMethod("sendFlowers", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。
我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。
注意:
生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了Proxy类,所以也就决定了java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。