java中的动态代理

Posted qunincey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中的动态代理相关的知识,希望对你有一定的参考价值。

我觉得要理解动态代理,首先要理解静态代理,因为动态代理是为了解决静态的问题才出现,详见上一篇静态代理的总结,直接看图

可以看出来,代理的实现就是这三方类,所以为了解决静态代理的弊端,需要在运行的时候动态的生成代理类。

而在java中jdk提供了proxy类创建动态类,jdk中是这样定义的

 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class<?>[] { Foo.class },
                                          handler);

先说明一下三个参数,第一个参数是被代理类的类加载器,第二个参数是被代理类的实例,最重要的是第三个参数,是一个InvocationHandler对象,最终我们要调用的方法就是要传入这个handler对象,

这个handle对象里面只有invoke方法

invoke(Object proxy, 方法 method, Object[] args)

处理代理实例上的方法调用并返回结果。 
这是在jdk上的解释,说白了,我们要做的业务逻辑就是写在这里,这个invoke里面也有三个参数,其实我觉得proxy类里面对这三个参数解释的挺清楚的了,
  • 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。

    第一个参数proxy是我们动态创建的代理对象,就是上面的第一句话,代理实例!第二参数就是目标方法的字节码对象,就是被代理方法的方法组成的数组,如果你不懂什么是字节码或者类加载器,你就直接当成方法数组就好了,最后一个参数代表是调用目标方法时参数。返回就是调用代理对象方法的返回返回就可以了

    /*
    * 接口
    * */
    public interface TargetInterface {
    
        public void method1();
        public String method2();
        public int method3(int x);
    }
    /*
    * 被代理类
    * */
    public class Target implements TargetInterface{
    
        @Override
        public void method1() {
            System.out.println("method1 running...");
        }
    
        @Override
        public String method2() {
            System.out.println("method2 running...");
            return "method2";
        }
    
        @Override
        public int method3(int x) {
            return x;
        }
    }
    /*
    * 测试动态代理
    * */
    public class ProxyTest {
    
        @Test
        public void test1(){
            //获得动态的代理对象----在运行时 在内存中动态的为Target创建一个虚拟的代理对象
            //objProxy是代理对象 根据参数确定到底是谁的代理对象
            TargetInterface objProxy = (TargetInterface) Proxy.newProxyInstance(
                    Target.class.getClassLoader(), //与目标对象相同的类加载器
                    new Class[]{TargetInterface.class}, 
                    new InvocationHandler() {
                        //invoke 代表的是执行代理对象的方法
                        @Override
                        //method:代表目标对象的方法字节码对象
                        //args:代表目标对象的响应的方法的参数
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("目标方法前的逻辑");
                            //执行目标对象的方法
                            Object invoke = method.invoke(new Target(), args);
                            System.out.println("目标方法后的逻辑");
                            return invoke;
                        }
                    }
                );
            
            objProxy.method1();
            String method2 = objProxy.method2();
            System.out.println(method2);
            
        }
        
    }

    这里面很多东西我已经注释了,比较难理解的可能是这一句

    Object invoke = method.invoke(new Target(), args);

    这句代码如果你了解反射的话,应该可以看出来,这其实就是做了一个反射,第一个参数就是被代理类的实例,args就是方法的参数,这句话其实就是调用被代理对象的方法。也就是说在这个invoke里面我们可以对对象进行增强,

    最后我再总结一下,动态代理底层应该使用了反射的原理,根据传进来的类的接口的字节码文件(即new Class<?>[] { Foo.class }参数),动态的创建一个代理类,当我们在调用这个代理类的方法的时候(即objProxy.method1();

    我们实际上调用了invoke方法,反射的使用了被代理类的方法。

 

以上是关于java中的动态代理的主要内容,如果未能解决你的问题,请参考以下文章

JAVA之AOP

java中的动态代理机制

(java反射-JDK动态代理)+CGLIB动态代理

java中的动态代理

Java中的JDK动态代理

java动态代理中的invoke方法是如何被自动调用的