Java的动态代理

来源:互联网 发布:淘宝卖家不参加双十一 编辑:程序博客网 时间:2024/06/17 21:16

Java的动态代理



1.什么是动态代理?
 (1)代理模式 
                  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类             预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联             关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委             托类的对象的相关方法,来提供特定的服务。 
 (2)按照代理的创建时期,代理类可以分为两种。 
                  静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文             件就已经存在了。 
                  动态代理:在程序运行时,运用反射机制动态创建而成。


2.原理剖析

         在Java的Java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类或动态代理对象.
         Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类.如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类:如果需要为一个或多个接口动态的创建实例,也可以使用Proxy类创建动态代理实例.

        Proxy提供了如下两个方法来创建动态代理类和动态代理实例:


               (1).  staticClass<?>getProxyClass(ClassLoader loader,Class <?>...interfaces):创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口.第一个ClassLoader指定生成动态代理类的类加载器.
               (2).  staticObject newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替代执行InvocationHandler对象的invoke方法.

实际上,即使采用第一种方法获取了一个动态代理类之后,当程序需要通过该代理类来创建对象时一样需要传入一个InvocationHandler对象.也就是说,系统生成的每一个代理对象都有一个与之关联的InvocationHandler对象.

        Proxy 的全部静态方法

         // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器

        static InvocationHandler getInvocationHandler(Object proxy) 


         // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

        static Class getProxyClass(ClassLoader loader, Class[] interfaces) 


       // 方法 3:该方法用于判断指定类对象是否是一个动态代理类

       static boolean isProxyClass(Class cl) 


        // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
        static Object newProxyInstance(ClassLoader loader, Class[] interfaces,  InvocationHandler h)


java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。


              (3) InvocationHandler 的核心方法

              // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
             // 第三个参数是方法的参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
           Object invoke(Object proxy, Method method, Object[] args)
每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都需要指定一个类装载器对象


3.实例

(1)定义一个接口

package com.zp.spring.aop.helloworld;public interface ArtithmeticCalculator {int add(int i,int j);int sub(int i,int j);int mul(int i,int j);int div(int i,int j);}

(2)定义接口实现类

package com.zp.spring.aop.helloworld;public class ArtithmeticCalculatorImpl implements ArtithmeticCalculator{@Overridepublic int add(int i, int j) {int result=i + j;return result;}@Overridepublic int sub(int i, int j) {int result=i - j;return result;}@Overridepublic int mul(int i, int j) {int result=i * j;return result;}@Overridepublic int div(int i, int j) {int result=i / j;return result;}}

(3)生成代理对象

package com.zp.spring.aop.helloworld;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;/** * 动态代理 * @author zp * */public class LoggingProxy {private ArtithmeticCalculator target;public LoggingProxy(ArtithmeticCalculator target) {this.target=target;}public ArtithmeticCalculator getLoggingProxy(){ArtithmeticCalculator proxy=null;//代理对象由哪一个类加载器负责加载ClassLoader loader=target.getClass().getClassLoader();//代理对象的类型,即其中有哪些方法Class[] interfaces=new Class[]{ArtithmeticCalculator.class};//当调用代理对象其中的方法时,该执行的代码InvocationHandler h=new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName=method.getName();//日志System.out.println("The method "+methodName+" begins with "+Arrays.asList(args));//执行方法Object result=method.invoke(target, args);//日志System.out.println("The method "+methodName+" ends with "+result);//System.out.println(result);return result;}};proxy=(ArtithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);      return proxy;}}

(4)测试

package com.zp.spring.aop.helloworld;import java.lang.reflect.Proxy;public class Test {public static void main(String[] args) {//ArtithmeticCalculator artithmeticCalculator=new ArtithmeticCalculatorImpl();ArtithmeticCalculator target=new ArtithmeticCalculatorImpl();ArtithmeticCalculator proxy=new LoggingProxy(target).getLoggingProxy();System.out.println(proxy.getClass().getName()); // System.out.println(Proxy.isProxyClass(proxy.getClass()));int result=proxy.add(1, 2);System.out.println("-->"+result);result=proxy.div(4, 2);System.out.println("-->"+result);}}


      总结:代理类中有所实现的接口中的方法,以及equals()、toString()、hashCode()方法。其中接口数最大不超过65535。每一个代理对象都有一个与之关联的InvocationHandler对象。程序执行代理对象的方法时,实际上都是执行InvocationHandler对象的invoke()方法.


参考自:点击打开链接

                        点击打开链接

                         点击打开链接

                 

原创粉丝点击