• 08.Java动态代理实现与原理分析之动态代理


      1.动态代理

      代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 比如说,想要在每个代理的方法前都加上一个处理方法:

    public void giveMoney() {
            //调用被代理方法前加入处理方法
            beforeMethod();
            stu.giveMoney();
        }

      这里只有一个giveMoney方法,就写一次beforeMethod方法,但是如果出了giveMonney还有很多其他的方法,那就需要写很多次beforeMethod方法,看看下面动态代理如何实现。

      2.动态代理简单实现

      在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

      创建一个动态代理对象步骤,具体代码见后面:

      (1)创建一个InvocationHandler对象

    //创建一个与代理对象相关联的InvocationHandler
     InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu)

      (2)使用Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass 

      Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});

       

      (3)获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor

    Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);

      (4)通过构造器constructor来创建一个动态实例stuProxy

    Person stuProxy = (Person) cons.newInstance(stuHandler);

      就此,一个动态代理对象就创建完毕,当然,上面四个步骤可以通过Proxy类的newProxyInstances方法来简化:

    //创建一个与代理对象相关联的InvocationHandler
      InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
    //创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
      Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

      通过班长需要帮学生代交班费来分析动态代理。

      首先是定义一个Person接口:

    /**
     * 创建Person接口
     * @author Gonjan
     */
    public interface Person {
        //上交班费
        void giveMoney();
    }

      创建需要被代理的实际类:

    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
        
        @Override
        public void giveMoney() {
            try {
              //假设数钱花了一秒时间
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           System.out.println(name + "上交班费50元");
        }
    }

      再定义一个检测方法执行时间的工具类,在任何方法执行前先调用start方法,执行后调用finsh方法,就可以计算出该方法的运行时间,这也是一个最简单的方法执行时间检测工具。

    public class MonitorUtil {
        
        private static ThreadLocal<Long> tl = new ThreadLocal<>();
        
        public static void start() {
            tl.set(System.currentTimeMillis());
        }
        
        //结束时打印耗时
        public static void finish(String methodName) {
            long finishTime = System.currentTimeMillis();
            System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
        }
    }

      创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后再nvoke方法中执行被代理对象target的相应方法。当然,在代理过程中,我们在真正执行被代理对象的方法前加入自己其他处理。这也是Spring中的AOP实现的主要原理,这里还涉及到一个很重要的关于java反射方面的基础知识。 

    public class StuInvocationHandler<T> implements InvocationHandler {
       //invocationHandler持有的被代理对象
        T target;
        
        public StuInvocationHandler(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() + "方法");
         */   
            //代理过程中插入监测方法,计算该方法耗时
            MonitorUtil.start();
            Object result = method.invoke(target, args);
            MonitorUtil.finish(method.getName());
            return result;
        }
    }

      做完上面的工作后,我们就可以具体来创建动态代理对象了,上面简单介绍了如何创建动态代理对象,我们使用简化的方式创建动态代理对象:

    public class ProxyTest {
        public static void main(String[] args) {
            
            //创建一个实例对象,这个对象是被代理的对象
            Person zhangsan = new Student("张三");
            
            //创建一个与代理对象相关联的InvocationHandler
            InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
            
            //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
            Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
    
           //代理执行上交班费的方法
            stuProxy.giveMoney();
        }
    }

      我们执行这个ProxyTest类,先想一下,我们创建了一个需要被代理的学生张三,将zhangsan对象传给了stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数了的,上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。

      运行结果:

  • 相关阅读:
    2019年----沉淀的一年
    条目八《永不建立auto_ptr的容器》
    条目七《如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉》
    条目六《当心C++编译器中最烦人的分析机制》
    条目五《尽量使用区间成员函数代替它们的单元素兄弟》
    cpu上下文切换
    条目四《用empty来代替检查size()是否为0》
    条目三《确保容器中的副本对象高效而正确》
    ORB与LBP、HOG
    C++
  • 原文地址:https://www.cnblogs.com/wangdh666/p/10758979.html
Copyright © 2020-2023  润新知