zoukankan      html  css  js  c++  java
  • 002 动态代理

    一 . 概述

      我们知道AOP底层使用的就是动态代理,在JDK中支持接口级别的动态代理,

        这里我们进行一下演示,目的是方面后面看源代码时变得简单一些.


    二 . 核心接口  

    public interface InvocationHandler {
    
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }

    上面的接口就是动态代理的核心接口,我们可以理解为一个方法级别的拦截.

      其中的参数,proxy表述代理对象,method表示被拦截的方法,args表示运行的方法的参数.

      这是一个回调的形式进行描述的.

    public class Proxy implements java.io.Serializable 
      public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException

    上面是Proxy的代理生成器,我们一般使用的核心方法newProxyInstance().

      这里我们需要介绍一下这些参数,loader 表示加载新动态生成字节码文件的类加载器.

      interfaces表示代理对象需要实现的接口,

      最后一个参数就是代理对象的回调的业务代码的实现.


    三.简单的演示

    接口:

    public interface Subject {
        void exec();
    }

    实现类:  

    public class RealSubject implements Subject {
        @Override
        public void exec() {
            System.out.println("do subjct");
        }
    }

    InvocationHandler : 

    //自定义的处理方法
    public class ProxyHandler implements InvocationHandler{
    
        private Object target ;
        //这里的构造方法表示对目标对象的一个强引用
        public ProxyHandler(Object target) {
            super();
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("方法运行执行拦截");
            //调用目标对象的方法
            method.invoke(target, args);
            System.out.println("方法运行之后进行拦截");
            System.out.println("看下我们新生成的代理对象的类型:==>" +proxy.getClass());
            return null;
        }
    
    }

    测试代码: 

    public class MainTest {
    
        public static void main(String[] args) {
            //构造类加载器
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            //创建目标对象
            Subject subject = new RealSubject();
            Subject proxy = (Subject) Proxy.newProxyInstance(loader, subject.getClass().getInterfaces(), new ProxyHandler(subject));
            //调用代理对象
            proxy.exec();
        }
    }

    运行结果:  

    方法运行执行拦截
    do subjct
    方法运行之后进行拦截
    看下我们新生成的代理对象的类型:==>class com.sun.proxy.$Proxy0

    我们发现了目标对象的功能被增强了,但是目标对象的代码是没有发生改变的.

      这就是动态代理带来的好处.


    四 .局限性

      JDK动态代理底层也是重新生成一个class文件的,这个class文件被类加载器加载到JVM之中.

      由于实现了接口的原因,这个代理对象对外就表现为接口的形态.

    但是,局限性就出现了,如果一个目标对象没有实现接口,那么应该怎么处理呢?

      无法处理,因此spring考虑使用cglib帮助实现类的代理.

      这里不像继续介绍cglib的代码,因为我们一般情况下是不会完成这样的底层代码的.

    记住,无论是JDK动态代理还是cglib代理,底层做的事情都是创建一个字节码文件,根据目标对象对外的接口,创建其实现方法的接口.

      因为有拦截回调的功能,才能对目标对象的方法进行增强.

      这也就是spring只支持方法级别的AOP的原因.

  • 相关阅读:
    opencv 图片像素x,y 访问!!!
    python numpy 三维数组 排序问题
    python+opencv水表识别
    tesseractOCR安装
    cookie 的寻找和使用以及页面滚动(python+selenium)
    你还在用a标签吗?——用button替代a
    js正则高级函数(replace,matchAll用法),实现正则替换(实测很有效)
    腾讯云服务器centos7.2+nginx(开启gzip压缩)+uwsgi+Django+react
    轮播图采用js、jquery实现无缝滚动和非无缝滚动的四种案例实现,兼容ie低版本浏览器
    webstorm 添加css前缀(兼容)自动添加
  • 原文地址:https://www.cnblogs.com/trekxu/p/9096064.html
Copyright © 2011-2022 走看看