Java动态代理
程序员文章站
2022-03-02 17:57:07
...
引用
代理模式的作用就是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。客户可以通过代理对象获得对目标对象的间接访问,这就是代理模式。
代理模式一般涉及到三个角色:
(1)抽象角色:声明真实对象和代理对象的共同接口。
(2)代理角色:代理对象角色内部包含有真实对象的引用,从而可以操作真实对象,代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
(3)真实对象:代理角色所代表的真实对象,是我们最终要引用的对象。
我们首先来看一个例子(例1)
(1)定义一个接口(抽象角色) package com.test.proxy; //Abstract role public interface Subject { public void request(); } (2)真实角色 package com.test.proxy; //Real role public class RealSubject implements Subject{ @Override public void request() { System.out.println("Real role!"); } } (3)代理角色 package com.test.proxy; //Proxy role public class ProxySubject implements Subject{ private RealSubject realSubjectObj; @Override public void request() { preRequest(); if (realSubjectObj == null) { realSubjectObj = new RealSubject(); } realSubjectObj.request(); postRequest(); } private void postRequest() { System.out.println("after"); } private void preRequest(){ System.out.println("before"); } } (4)客户端 package com.test.proxy; public class Client { public static void main(String[] args) { Subject sub = new ProxySubject(); sub.request(); } }
通过以上代码,我们可以通过ProxySubject的实例作为代理访问RealSubject的request()方法。并且代理对象的request方法在调用真实对象的request()方法时,再前后都加了一定的处理。
如果按照例1中的方法是用代理模式,那么真实角色必须事先已经存在,并且将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理。如果与100个真实角色的类,你就必须相应的创建100个代理类,这样就会导致类的急剧膨胀;此外,如果事先并不知道真实角色所对应的类,该如何去定义代理类呢?这个问题可以通过Java的动态代理机制来解决。
引用
所谓动态代理(Dynamic Proxy),就是一种在运行时生成的class(代理类),在生成时它必须被提供一组interface(真实类的接口)给它,然后该生成的代理类(class)就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的时候你必须提供一个handler,由它接管实际的工作。
具体创建动态代理并调用代理类可以分为3步:
(1)通过实现InvocationHandler接口创建自己的调用处理器;
这个调用处理器会作为创建代理类的一个参数。通过这个调用处理器实例作为其中一个参数创建出来的代理类,会被client调用。在client调用生成都动态代理类的方法时(这个方法必须是真实类所实现接口的方法;代理类的接口包含真实类的全部接口Subject),便会调用调用器的invoke方法。
(2)通过为Proxy类指定ClassLoader对象,一组interface类,和第一步创建的调用器的实例来创建动态代理类的实例。
(3)调用代理类实例的方法。(这个方法一定是真实类所实现接口的方法)
我们可以通过以下代码更直观的看一下:
//创建接口Subject package com.test.dynamic.proxy; public interface Subject { public void request(); } //创建真实角色类,它实现了Subject接口 package com.test.dynamic.proxy; public class RealSubject implements Subject { @Override public void request() { System.out.println("doing something really"); } } //创建调用器 package com.test.dynamic.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 该代理类内部属性为Object类,实际使用时通过该类的构造方法对其赋值, * 此外,该类还实现了invoke方法。其实就是调用被代理对象将要执行的方法。 * args为执行被代理对象方法所需的参数。 * 通过动态代理,我们可以在调用之前或之后进行一些相关操作。 * * @author Administrator * */ public class DynamicSubject implements InvocationHandler{ /** * 声明目标对象也就是被代理对象 */ private Object target; /** * 获得代理对象, * 这个代理对象是由传入的接口类型动态构造出来的一个代理类实例类型, * 这个代理类是JVM在内存中动态构造的动态类 * * tar.getClass().getClassLoader() 代理类加载器需和目标对象类加载器一致 * tar.getClass().getInterfaces() 代理对象需实现目标对象所实现的接口 * this 调用此方法对象本身 * @param tar 被代理对象 * @return */ public Object getProxy(Object tar) { this.target = tar; Object proxy = Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this); return proxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("before calling " + method); try { result = method.invoke(target, args);//调用被代理类方法 } catch (Exception e) { e.printStackTrace(); } System.out.println("after calling " + method); return result; } } //客户端 package com.test.dynamic.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { Subject rs = new RealSubject(); InvocationHandler ds = new DynamicSubject(rs); Subject subject = (Subject)ds.getProxy(rs) ; subject.request(); } }
通过以上方法,我们就可以的动态的创建代理类了。也就不用我们为每一类创建它所对应的代理类了。并且通过调用由同一个InvocationHandler生成的代理类的方法时,都会在调用真实对象方法的前后做各种处理(见invoke方法)。Struts和Spring都用到了这种模式来动态创建类并实现了Filter和AOP技术。
关于创建动态代理类实例的过程,我们可以再细分一下:
(1)通过实现InvocationHandler接口创建自己的调用处理器;
InvocationHandler ds = new DynamicSubject(rs);
(2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类;
// java.lang.refelect.Proxy类的静态方法 //其中loader是真实角色类的classloader, interfaces是真实角色类所实现的interface数组。 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
(3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
private final static Class[] constructorParams ={ InvocationHandler.class } Constructor cons = cl.getConstructor(constructorParams); //返回的就是java.lang.reflect.Proxy子类的构造器,也就是说动态代理类就是java.lang.reflect.Proxy的子类。
(4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
proxyObj = (Object) cons.newInstance(new Object[] { ds })