【JAVA】JDK动态代理初探之Proxy.newProxyInstance

JDK的动态代理这个词大佬们经常挂在嘴边,可我却是云里雾里。
这几个字分开倒是都认识,组合在一起就不明白它是表达的什么意思了

talk is cheap, show me the code.

基础实战

main入口类

  • 新建一个含有main方法的测试类TestMain.java

    package com.itplh.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    /**
     * JDK动态代理
     * Proxy 代理的本质是方法的拦截
     *
     * $Proxy0 extends Proxy implements Student,我们看到代理类继承了Proxy类,
     * 所以也就决定了java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理
     *
     * 因为JDK的动态代理只能基于接口,所以 newProxyInstance 的第二个参数 Class<?>[] interfaces 的元素必须是接口
     *
     *
     * @author: tanpeng
     * @since: 2020-06-02 9:57
     */
    public class TestMain {
        public static void main(String[] args) {
            // 使用匿名内部类的方式声明一个Teacher接口的实现类
            Student student = () -> "I'm Student interface of implements class.";
    
            // 一、Proxy.newProxyInstance + 匿名内部类
            Student studentProxy1 = (Student) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class[]{Student.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            print("Proxy.newProxyInstance + 匿名内部类",
                                    (String) method.invoke(student, args),
                                    method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
                            return null;
                        }
                    });
            studentProxy1.hi();
    
            // 二、Proxy.newProxyInstance + lambda表达式
            Student studentProxy2 = (Student) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class[]{Student.class},
                    (proxy, method, args1) -> {
                        print("Proxy.newProxyInstance + lambda表达式.",
                                (String) method.invoke(student, args1),
                                method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
                        return null;
                    });
            studentProxy2.hi();
    
            // 三、Proxy.newProxyInstance + InvocationHandler实现类
            Student studentProxy3 = (Student) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class[]{Student.class},
                    new TestInvocationHandler<Student>(student));
            studentProxy3.hi();
    
            // 四、封装ProxyFactory 代理Student接口
            Student studentProxy4 = TestProxyFactory.getProxyObject(student);
            studentProxy4.hi();
    
            // 五、封装ProxyFactory 代理Teacher接口
            Teacher teacher = () -> "I'm teacher.";
            Teacher teacherProxy = TestProxyFactory.getProxyObject(teacher);
            teacherProxy.hi();
    
            // 这里传实现类 ZhangSan.class 将会报错
            // Exception in thread "main" java.lang.IllegalArgumentException: com.itplh.proxy.ZhangSan is not an interface
    //        Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
    //                new Class[]{ZhangSan.class},
    //                new TestInvocationHandler<Student>(student));
        }
    
        public static void print(String... text) {
            System.out.println("------------------------------------------");
            Arrays.asList(text).forEach(t -> System.out.println("------ " + t));
            System.out.println("------------------------------------------");
            System.out.println();
        }
    }
    
    interface Student {
        @TestAnnotation("select * from student where id = #{id}")
        String hi();
    }
    
    class ZhangSan implements Student {
        @Override
        public String hi() {
            return "I'm zhangsan.";
        }
    }
    
    interface Teacher {
        @TestAnnotation("select * from teacher where id = #{id}")
        String hi();
    }
    
  • 控制台输出

    ------------------------------------------
    ------ Proxy.newProxyInstance + 匿名内部类
    ------ I'm Student interface of implements class.
    ------ SELECT * FROM STUDENT WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ Proxy.newProxyInstance + lambda表达式.
    ------ I'm Student interface of implements class.
    ------ SELECT * FROM STUDENT WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ InvocationHandler of implements class.
    ------ I'm Student interface of implements class.
    ------ SELECT * FROM STUDENT WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.itplh.proxy.Student.
    ------ I'm Student interface of implements class.
    ------ SELECT * FROM STUDENT WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.itplh.proxy.Teacher.
    ------ I'm Teacher interface of implements class.
    ------ SELECT * FROM TEACHER WHERE ID = #{ID}
    ------------------------------------------
    

其他类

  • 自定义一个注解TestAnnotation.java

    package com.itplh.proxy;
    
    import java.lang.annotation.*;
    
    /**
    * @author: tanpeng
    * @since: 2020-06-02 10:09
    */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface TestAnnotation {
       String[] value();
    }
    
  • 自定义TestInvocationHandler.java

    package com.itplh.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 动态代理-拦截器
     * 作用:控制目标对象的目标方法的执行
     * 
     * @author: tanpeng
     * @since: 2020-06-02 9:57
     */
    public class TestInvocationHandler<T> implements InvocationHandler {
    
        // InvocationHandler持有的被代理对象
        private T target;
    
        public TestInvocationHandler(T target) {
            this.target = target;
        }
    
        /**
         * 调用被代理的对象的方法都会进入本方法
         *
         * @param proxy  代表动态代理对象
         * @param method 代表正在执行的方法
         * @param args   代表调用目标方法时传入的实参
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            TestMain.print("InvocationHandler of implements class.",
                    (String) method.invoke(target, args),
                    method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
            return null;
        }
    }
    
  • 封装一个代理工厂TestProxyFactory.java

    package com.itplh.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    /**
     * @author: tanpeng
     * @since: 2020-06-02 11:08
     */
    public class TestProxyFactory {
    
        /**
         * static <T> 表示将该方法声明为泛型方法
         *
         * @param target 目标代理对象
         * @return
         */
        public static <T> T getProxyObject(T target) {
            return getProxyObject(target, (proxy, method, args) -> {
                TestMain.print("ProxyFactory " + target.getClass().getInterfaces()[0].getName() + ".",
                        (String) method.invoke(target, args),
                        method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
                return null;
            });
        }
    
        /**
         * static<T> 表示将该方法声明为泛型方法
         *
         * @param target 目标代理对象
         * @param invocationHandler
         * @return
         */
        public static <T> T getProxyObject(T target, InvocationHandler invocationHandler) {
            return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    target.getClass().getInterfaces(),
                    invocationHandler);
        }
    }
    

测试含有多个方法的接口

  • 新建接口People 、User,及实现类DefaultUser

    interface People {
        @TestAnnotation("String hi();")
        String hi();
    }
    
    interface User extends People {
        @TestAnnotation("select username from user where id = #{id}")
        String selectUsernameById(String id);
    
        @TestAnnotation("select email from user where id = #{id}")
        String selectEmailById(String id);
    }
    
    class DefaultUser implements User {
        @Override
        public String hi() {
            return "This is DefaultUser#hi.";
        }
        
        @Override
        public String selectUsernameById(String id) {
            return "tanpeng";
        }
        
    	@Override
        public String selectEmailById(String id) {
            return "[email protected]";
        }
    }
    
  • main 函数中测试

    DefaultUser user = new DefaultUser();
    
    User userProxy = TestProxyFactory.getProxyObject(user);
    userProxy.hi();
    userProxy.selectUsernameById("1");
    userProxy.selectEmailById("1");
    
    People peopleProxy = TestProxyFactory.getProxyObject(user);
    peopleProxy.hi();
    ((User) peopleProxy).selectUsernameById("1");
    ((User) peopleProxy).selectEmailById("1");
    
  • 控制台输出

    ------------------------------------------
    ------ ProxyFactory com.aden.modules.datax.controller.User.
    ------ This is DefaultUser#hi.
    ------ STRING HI();
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.aden.modules.datax.controller.User.
    ------ tanpeng
    ------ SELECT USERNAME FROM USER WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.aden.modules.datax.controller.User.
    ------ tanpeng@itplh.com
    ------ SELECT EMAIL FROM USER WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.aden.modules.datax.controller.User.
    ------ This is DefaultUser#hi.
    ------ STRING HI();
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.aden.modules.datax.controller.User.
    ------ tanpeng
    ------ SELECT USERNAME FROM USER WHERE ID = #{ID}
    ------------------------------------------
    
    ------------------------------------------
    ------ ProxyFactory com.aden.modules.datax.controller.User.
    ------ tanpeng@itplh.com
    ------ SELECT EMAIL FROM USER WHERE ID = #{ID}
    ------------------------------------------
    

应用场景

  • 通过代理对类进行增强
    设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放。
    我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit)。
    这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
  • AOP
  • 松散耦合
    在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。
    那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,
    也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

知识拓展

  • 静态代理
    在代码编译时就确定了被代理的类是哪一个

  • 动态代理
    在代码运行期间才加载被代理的类,编译时并未确定被代理的类是哪一个

  • 静态代理动态代理的区别
    代理涉及到两个关联词代理类委托类
    静态代理 一个代理类针对一个委托类,即一对一
    动态代理 一个代理类可利用反射机制代理多个委托类,即一对多

  • 两种动态代理的区别

    • JDK的动态代理 基于接口的代理

      • 代理对象和目标对象实现了共同的接口
      • 拦截器必须实现InvocationHanlder接口
    • cglib的动态代理 基于类的代理 (Code Generation Library )

      • 代理对象是目标对象的子类
      • 拦截器必须实现MethodInterceptor接口
  • 动态代理的相关问题

    • 代理对象是由谁产生的
      JVM产生的,不像静态代理,我们自己得new个代理对象出来。

    • 代理对象实现了什么接口
      实现的接口是目标对象实现的接口。同静态代理中代理对象实现的接口。那个继承关系图还是相同的。
      代理对象目标对象都实现一个共同的接口。所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。

    • 代理对象的方法体是什么
      代理对象的方法体中的内容就是拦截器中invoke方法中的内容。
      所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在invoke方法里面处理的。

    • 拦截器中的invoke方法中的method参数是在什么时候赋值的
      代理对象调用目标方法的时候。
      如,studentProxy1.hi();userProxy.selectUsernameById("1");
      这时实际上进入的是拦截器中的invoke方法,这个时候拦截器中的invoke方法中的method参数会被赋值。

动态代理对象时用JDK的相关代码生成的,叫JDK动态代理
动态代理对象时用cglib的 jar 包,叫cglib动态代理

参考

猜你喜欢

转载自blog.csdn.net/AV_woaijava/article/details/106494690
今日推荐