浏览 4904 次
锁定老帖子 主题:动态代理(proxy)类和类加载器
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2013-05-06
最后修改:2013-05-06
可以在运行时创建一个实现了一组给定接口的新类(代理类)。不过该代理类和Hibernate中使用的代理类使用的是不一样的技术。 该代理类具有下列方法: 指定接口所需要的全部方法 Object类中的全部方法 但是该代理类不能定义其所实现接口的方法,即,不能编写实现该方法的代码。所以需要invocation handler,该invocation handler实现了InvocationHandler接口的类对象,该接口中方法的定义: Object invoke(Object proxy, Method method, Object[] args) 在代理实例上处理方法调用并返回结果。 在代理实例上处理方法调用并返回结果。 那么当我们调用新生成的代理对象的方法时,invocation handler的invoke方法就会被调用。从而间接达到了为代理类增加自己代码的目的。 以下面这个例子为例,我们希望实现Comparable接口的对象的compareTo方法被调用时执行一些额外的代码,如打印被调用的方法(compareTo)名称、调用该方法传递进来的参数。我们可以使用代理类实现。具体代码详见《java核心技术 卷1:基础知识》接口与内部类这章的代理小节中的例6-7, ProxyTest.java代码。 为了做实验我们编写自己的代码进行操作,有如下接口与类: public interface ShapeInterface { public double getArea(); } Cricle、Square、Triangle类实现了ShapeInterface接口。 我们希望实现如下功能,当ShapeInterface的实现类的getArea()方法被调用的时候,打印出被代理对象的类型、该代理对象中被调用的方法名、参数个数、参数类型、参数值,如果没有参数传递进来则打印“该方法没有参数”,最后执行getArea()方法本身。这些功能可以算是为代理类新增的代码,但我们不能直接在代理类中编写代码,我们需要为ShapeInterface接口定义一个invocation handler,即下面的ShapeProxyHandler,见如下代码: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ShapeProxyHandler implements InvocationHandler { public ShapeProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print("你所要代理的对象类型是"+target.getClass().getName()); System.out.println(",要执行"+method.getName()+"方法"); if (args!=null){ System.out.print("该方法有"+args.length+"个参数,分别是"); for(Object e:args){ System.out.print(e.getClass().getName()); System.out.print(","+e); } } else{ System.out.println("该方法没有参数"); } return method.invoke(target, args); } private Object target;//存放要代理的对象 } 该invocation handler编写好以后,编写如下测试代码进行测试: import java.lang.reflect.Proxy; import proxy.ShapeInterface; public class ProxyTestShape { public static void main(String[] args) { Object[] elements = new Object[3];//用于存放代理对象的数组 ShapeProxyHandler proxyHandler1 = new ShapeProxyHandler(new Circle()); Object proxy1 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1); elements[0] = proxy1; ShapeProxyHandler proxyHandler2 = new ShapeProxyHandler(new Square(10)); Object proxy2 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler2); elements[1] = proxy2; ShapeProxyHandler proxyHandler3 = new ShapeProxyHandler(new Triangle(10, 20, 30)); Object proxy3 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler3); elements[2] = proxy3; invokeGetArea(elements); } public static void invokeGetArea(Object[] elements){ for(Object e:elements){ System.out.println("返回结果是="+((ShapeInterface)e).getArea()); System.out.println("===============END============="); } } } 其中下面这句代码: Object proxy1 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1); 使用了Proxy类的newProxyInstance方法。 我们先来看一下该方法第一个参数: 类加载器(class loader),ShapeInterface.class.getClassLoader()就是ShapeInterface这个类型的类加载器。该处代码和java核心技术的参考代码又不一样,核心技术里面该处的参数为null,主要是因为核心技术里面实现的是Comparable接口,而该接口是被Extension类加载器。Extension类加载器用于从jre/lib/ext目录加载,而Comparable作为java.lang包中的接口正好位于jre/lib/ext目录。但我们自定义的接口ShapeInterface却不能被Extension类加载器加载而是被System类加载器加载,所以此处的代码不能像java核心技术中的那样设置为null。每个线程都有一个对类加载器的引用,称为上下文类加载器。主线程的上下文类加载器是System类加载器。在这里main方法所在的主线程的就使用了System类加载器。也就是说主线程的上下文类加载器和自定义接口的ShapeInterface类加载器刚好一样,都是System类加载器。那么上面这段代码可以改成如下这段代码: Object proxy1 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1); 其中Thread.currentThread().getContextClassLoader()这句代码就是获得当前线程(主线程)的上下文类加载器。 我们再来看一下该方法第二个参数: 这个参数new Class[] { ShapeInterface.class }是一个数组,意味着我们实际上生成的代理类可以实现多个接口。 该方法的第三个参数: proxyHandler1就是我们实现的invocation handler,也就是在这个invocation handler中我们编写了想要为代理类增加的代码。 使用代理类还应注意,我们只能生成接口的代理类而不能生成某个类的代理类。 代理类的其他案例: 实际开发中可能会遇到新旧版本的匹配问题,如旧版本接口的和新版本的接口不一样,但是希望新旧版本都可使用,也可以使用动态代理类实现。具体请见参考资料3. 代理技术为Java提供了极大的动态性可以使我们的编程更加灵活,所以有兴趣的话大家可以了解一下动态代理类。 参考代码见附件 参考资料: 1. JAVA核心技术 卷1:基础知识--接口与内部类--代理小节 2. JAVA核心技术 卷2:高级特性--安全--类加载器小节 3.深入理解Java 7:核心技术与最佳实践 成富 著 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2013-05-07
proxyHandler1就是我们实现的invocation handler
|
|
返回顶楼 | |
发表时间:2013-05-09
哥们,用cglib吧,java的proxy要写那么多接口,太笨重了,cglib是动态生成类的
|
|
返回顶楼 | |
发表时间:2013-05-13
414149609 写道 哥们,用cglib吧,java的proxy要写那么多接口,太笨重了,cglib是动态生成类的
非常感谢指点,的确jdk本身的动态代理有点复杂 再问问,现在一般来说处理xml的用什么,用dom4j吗? |
|
返回顶楼 | |