设计模式之代理模式
代理模式有静态代理和动态代理
静态代理:提到代理,我们首先想到的就是代理商了,代理商就是替厂家卖产品,这里有三个关键的地方,第一点,卖产品的行为是代理商做的(至少对于消费者来说,消费者接触的就是代理商);第二产品是厂家的(也就是说代理商卖的商品是厂家的,而代理商是没有产品的,从这点来说实质上还是厂家再卖产品)。
回到我们的静态代理上,静态代理有三个角色,委托类(相当于厂家),代理类(相当于代理商),委托类需要实现的接口(产品,只是产品将厂家和代理商联系了起来,同样也是接口将委托类和代理类联系起来了)。了解了这些,静态代理地UML图为:
1.RealSubject是委托类,Proxy是代理类
2.Subject是委托类和代理类实现的接口
3.request()是委托类和代理类的共同方法,相当于卖产品这种行为。
具体代码如下:
1 //接口 2 interface Subject { 3 void request(); 4 } 5 6 //委托类 7 class RealSubject implements Subject { 8 9 public void request(){ 10 System.out.println("RealSubject"); 11 } 12 } 13 14 //代理类 15 class Proxy implements Subject { 16 private Subject subject; 17 //代理的行为(方法) 18 public Proxy(Subject subject){ 19 this.subject = subject; 20 } 21 public void request(){ 22 System.out.println("begin"); 23 subject.request(); 24 System.out.println("end"); 25 } 26 } 27 28 //测试类 29 public class ProxyTest { 30 public static void main(String args[]) { 31 RealSubject subject = new RealSubject(); 32 Proxy p = new Proxy(subject); 33 p.request(); 34 } 35 }
静态代理实现中,代理类在编译期间就已经确定(程序员直接写了出来)。
动态代理:
代理对象如何产生?
(代理对象的)方法执行过程(如何进行代理的)?进去再出来机制,进去就是利用接口给类传参数来产生需要的代理类,出来就是通过接口,代理类执行自己写的方法。
主要体现在Proxy类中产生代理类的静态方法public static Object getInstance(Class[] interfac,MethodHandler h)
出来时,代理类中,在代理方法中通过MethodHandler变量h ,调用h.invoke(this,Method m);这时方法便执行实现了(自己写的)MethodHandler类的invoke(Object,Method)方法,在MethodHandler类中,我们实现插入的逻辑。
动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以代理任何类所实现的接口的任何方法,进行任意的处理。例如:可以代理所有的类而不用像静态代理那样为每个类生成一个代理类,而是动态生成代理类。修改代理的逻辑时(如将时间代理换成日志代理)而不同修改代理类的逻辑,也就是说更加灵活。动态代理更加灵活,代码重用性好,模块化,低内聚。
接下来,用具体类分析:
1.定义委托类实现的接口(接口中的方法也就是要代理的方法)和委托类
1 package dynamic_proxy2; 2 3 public interface Service { 4 public void add(); 5 }
1 package dynamic_proxy2; 2 3 public class UserServiceImpl implements Service{ 4 public void add(){ 5 System.out.println("this is an add()"); 6 } 7 8 }
2.利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口生成代理
1 package dynamic_proxy2; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 public class MyInvocationHandler implements InvocationHandler{ 8 private Object target;//委托对象 9 10 public MyInvocationHandler(Object target){ 11 this.target = target; 12 } 13 14 /** 15 * 此方法由生成的代理类调用,并生成调用方法的方法对象,并传入到此方法,此后在此方法中由原生类进行反射调用 16 */ 17 @Override 18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 19 System.out.println("add() begin"); 20 Object result = method.invoke(target,args);//反射 21 System.out.println("add() end"); 22 return result; 23 } 24 25 public Object getProxy(){ 26 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 27 Class<?>[] interfaces = target.getClass().getInterfaces(); 28 29 /** 30 * classLoader:用ContextClassLoader进行对代理类进行加载 31 * interfaces:原生类实现的接口 32 * this:实现了InvocationHandler接口的MyInvocationHandler(自己写的), 33 * 这个对象对传入到代理类的InvocationHandler变量中 34 */ 35 return Proxy.newProxyInstance(classLoader, interfaces, this); 36 } 37 38 }
3.使用动态代理:
1 package dynamic_proxy2; 2 3 public class ProxyTest { 4 5 public static void main(String[] args) { 6 Service service = new UserServiceImpl();//原生类 7 8 MyInvocationHandler handler = new MyInvocationHandler(service); 9 Service serviceproxy = (Service) handler.getProxy();//获得代理对象 10 11 /** 12 * 生成add()对象 m,并调用InvocationHandler变量的invoke(this,m) 13 */ 14 serviceproxy.add(); 15 } 16 17 }
以上的这些也就是程序员需要编写的,动态代理的核心Proxy类和InvocationHandler接口已经由JDK实现了。
接下来看看运行结果
1 add() begin 2 this is an add() 3 add() end
然后我们借用这个例子再具体说说,动态代理的特点与好处:
1.若我们想对任何的类进行代理,我们只要创建这个类即可,然后生成该类的对象作为参数传给实现了InvocationHandler接口的MyInvocationHandler类的构造器方法。
2.若我们相对委托类进行任何的处理只需在MyInvocationHandler类中的invoke方法(此方法即是实现的InvocationHandler的方法)中写入处理代码。
了解了这些后,我们就该谈谈代理类是如何产生的?以及代理类如何进行代理的?
首先,我们来谈谈代理类的产生,代理类(对象)的生成过程是由Proxy类的newProxyInstance方法实现,分为三个步骤:
1.ProxyGenerator.generateProxyClass方法负责生成代理类的字节码,在我的模拟JDK动态代理实现中,是生成代理类源码,这个代理类源码由委托类实现的接口,类加载器组装而成。
2.native方法Proxy.defineClass0负责字节码的加载,并返回对应的Class对象。在模拟实验中,是利用Java提供的编译器,加载器,对代理类进行编译和加载。
3.利用反射机制生成代理类的对象。在模拟实验中,通过反射机制,得到带有委托类实现接口参数的构造器对象,并利用这个对象生成代理类的对象。代码如:
1 //ul为类加载器,方法里的参数为生成的代理类的源码 2 Class c = ul.loadClass("com.bjsxt.proxy.TankTimeProxy"); 3 //Moveable为委托类,代理类实现的接口 4 Constructor ctr = c.getConstructor(Moveable.class); 5 //得到代理类的对象 6 Moveable m = (Moveable)ctr.newInstance(new Tank());
通过反编译得到代理类的源码:
1 public final class $proxy1 extends Proxy implements Service { 2 3 public $proxy1(InvocationHandler invocationhandler) { 4 super(invocationhandler); 5 } 6 7 public final boolean equals(Object obj) { 8 try { 9 return ((Boolean)super.h.invoke(this, m1, new Object[] { 10 obj 11 })).booleanValue(); 12 } 13 catch(Error _ex) { } 14 catch(Throwable throwable) { 15 throw new UndeclaredThrowableException(throwable); 16 } 17 } 18 19 public final String toString() { 20 try { 21 return (String)super.h.invoke(this, m2, null); 22 } 23 catch(Error _ex) { } 24 catch(Throwable throwable) { 25 throw new UndeclaredThrowableException(throwable); 26 } 27 } 28 29 public final void add() { 30 try { 31 super.h.invoke(this, m3, null); 32 return; 33 } 34 catch(Error _ex) { } 35 catch(Throwable throwable) { 36 throw new UndeclaredThrowableException(throwable); 37 } 38 } 39 40 public final int hashCode() { 41 try { 42 return ((Integer)super.h.invoke(this, m0, null)).intValue(); 43 } 44 catch(Error _ex) { } 45 catch(Throwable throwable) { 46 throw new UndeclaredThrowableException(throwable); 47 } 48 } 49 50 private static Method m1; 51 private static Method m2; 52 private static Method m3; 53 private static Method m0; 54 55 static { 56 try { 57 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { 58 Class.forName("java.lang.Object") 59 }); 60 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 61 m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]); 62 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 63 } 64 catch(NoSuchMethodException nosuchmethodexception) { 65 throw new NoSuchMethodError(nosuchmethodexception.getMessage()); 66 } 67 catch(ClassNotFoundException classnotfoundexception) { 68 throw new NoClassDefFoundError(classnotfoundexception.getMessage()); 69 } 70 } 71 } 72
根据我们前面的分析,生成的代理类中应该有InvocationHandler变量,实现了代理方法会调用子类(也就是我们实现InvocationHandler接口的类)的invoke方法。
而从上面的代码中并没有发现有InvocationHandler变量,但是它(生成的代理类)继承自Proxy类,接下来看一下Proxy类
1 public class Proxy implements java.io.Serializable { 2 3 private static final long serialVersionUID = -2222568056686623797L; 4 5 /** parameter types of a proxy class constructor */ 6 private static final Class<?>[] constructorParams = 7 { InvocationHandler.class }; 8 9 /** 10 * a cache of proxy classes 11 */ 12 private static final WeakCache<ClassLoader, Class<?>[], Class<?>> 13 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 14 15 /** 16 * the invocation handler for this proxy instance. 17 * @serial 18 */ 19 protected InvocationHandler h;
在代码的最后,我们发现了InvocationHandler变量h。由此代理方法的整个调用过程就是,
1,代理类调用代理方法,在代理方法内,得到该方法对象,
2,调用InvocationHandler变量h的invoke方法,传入代理类,代理方法对象,代理方法参数,
此时h引用的是我们实现InvocationHandler接口的子类,这步操作是在zileidegetProxy()方法里
1 public Object getProxy(){ 2 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 3 Class<?>[] interfaces = target.getClass().getInterfaces(); 4 5 /** 6 * classLoader:用ContextClassLoader进行对代理类进行加载 7 * interfaces:原生类实现的接口 8 * this:实现了InvocationHandler接口的MyInvocationHandler(自己写的), 9 * 这个对象对传入到代理类的InvocationHandler变量中 10 */ 11 return Proxy.newProxyInstance(classLoader, interfaces, this); 12 }
super.h.invoke(this, m3, null);
JDK动态代理的局限性
通过反射类Proxy和InvocationHandler接口实现的动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类
便无法使用这种方式实现动态代理。而且就算实现了接口也只能代理实现了接口的方法,委托类原有的类仍然不能被代理。