美文网首页
jdk 动态代理

jdk 动态代理

作者: hehehehe | 来源:发表于2020-11-10 13:22 被阅读0次

    比代理模式更强大的是我们只需要定义接口,用动态代理类当做动态实现接口 mybatis mapper接口的实现
    https://zhuanlan.zhihu.com/p/62660956

    静态代理


    image.png

    动态代理 https://www.jianshu.com/p/dcd7ce106370
    1、包:如果所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;
    2、类修饰符:该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;
    3、类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
    4、类继承关系:该类的继承关系如图:

    image.png
    动态代理的特点

    1.继承自 java.lang.reflect.Proxy,实现了 Rechargable,Vehicle 这两个ElectricCar实现的接口;
    2.类中的所有方法都是final 的;
    3.所有的方法功能的实现都统一调用了InvocationHandler的invoke()方法。
    JDK动态代理的缺点
    JDK动态代理仅支持 interface 代理的桎梏,因为动态生成的代理类的继承自 Proxy,Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。

    public interface Subject {
        public void print(String msg);
    }
    
    public class RealSubject implements Subject {
        @Override
        public void print(String msg) {
            System.out.println("real subject");
        }
    }
    
    public class JdkProxySubject implements InvocationHandler {
    
        private Subject subject;
    
        public JdkProxySubject(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object result = null;
            try {
                result = method.invoke(subject, args);
            } catch (Exception e) {
                throw e;
            } finally {
                System.out.println("after");
            }
            return result;
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            Subject subject = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, new JdkProxySubject(new RealSubject()));
            subject.print("234");
        }
    }
    
    
    image.png

    1.原先我们本打算直接根据接口Class得到代理对象,无奈接口Class只有方法信息,没有构造器
    2.于是,我们想,有没有办法创建一个Class对象,既有接口Class的方法信息,同时又包含构造器方便创建代理实例呢?
    3.利用Proxy类的静态方法getProxyClass()方法,给它传一个接口Class对象,它能返回一个加强版Class对象。也就是说getProxyClass()的本质是:用Class,造Class。

    Class<?> proxyClass = Proxy.getProxyClass(Client.class.getClassLoader(), Subject.class);
    Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
    Subject subject2 = (Subject)constructor.newInstance(new InvocationHandler() {
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
          return null;
          }
    });
    System.out.println(123);
    
    动态代理底层调用逻辑
    image.png

    为什么这么设计?
    为了解耦,也为了通用性。
    如果JVM生成代理对象的同时生成了特定逻辑的方法体,那这个代理对象后期就没有扩展的余地,只能有一种玩法。而引入InvocationHandler的好处是:
    JVM创建代理对象时不必考虑方法实现,只要造一个空壳的代理对象,舒服
    后期代理对象想要什么样的方法实现,我写在invocationHandler对象的invoke()方法里送进来便是
    所以,invocationHandler的作用,倒像是把“方法”和“方法体”分离。JVM只造一个空的代理对象给你,后面想怎么玩,由你自己组装。反正代理对象中有个成员变量invocationHandler,每一个方法里只有一句话:handler.invoke()。所以调任何一个代理方法,最终都会跑去调用invoke()方法。

    日志打印
    public interface Advice {
        void beforeMethod(Method method);
        void afterMethod(Method method);
    }
    public class MyLogger implements Advice {
    
        public void beforeMethod(Method method) {
            System.out.println(method.getName() + "方法执行开始...");
        }
    
        public void afterMethod(Method method) {
            System.out.println(method.getName() + "方法执行结束...");
        }
    }
    public class ProxyTest {
        public static void main(String[] args) throws Throwable {
            CalculatorImpl target = new CalculatorImpl();
            Calculator calculatorProxy = (Calculator) getProxy(target, new MyLogger());
            calculatorProxy.add(1, 2);
            calculatorProxy.subtract(2, 1);
        }
    
        private static Object getProxy(final Object target, Advice logger) throws Exception {
            /*代理对象的方法最终都会被JVM导向它的invoke方法*/
            Object proxy = Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),/*类加载器*/
                    target.getClass().getInterfaces(),/*让代理对象和目标对象实现相同接口*/
                    (proxy1, method, args) -> {
                        logger.beforeMethod(method);
                        Object result = method.invoke(target, args);
                        System.out.println(result);
                        logger.afterMethod(method);
                        return result;
                    }
            );
            return proxy;
        }
    }
    
    image.png image.png

    相关文章

      网友评论

          本文标题:jdk 动态代理

          本文链接:https://www.haomeiwen.com/subject/dzfjbktx.html