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

    1、前言

      java中代理方式分为静态代理和动态代理,静态代理的代理关系在编译时就确定了,它需要为每一个目标类创建一个代理类,在代理类数量较少时可以选择使用。当代理类较多时,需要使用动态代理,动态代理相对来说提供了很大的灵活性,以下讲解下动态代理的两种实现方式,即JDK原生动态代理和CGLIB动态代理

    2、JDK原生动态代理

    2.1 示例代码

    定义接口:

    public interface Fruit {
    
        String name(final String name);
    
        double price(double price);
    }

    实现接口的具体代理对象类:

    public class Apple implements Fruit {
    
        @Override
        public String name(String name) {
            System.out.println("this is " + name);
            return name;
        }
    
        @Override
        public double price(double price) {
            System.out.println("Apple price is " + price);
            return price;
        }
    }

    定义实现接口InvocationHandler的类:

    public class FruitDynamicProxy implements InvocationHandler {
    
        private Fruit fruit;
    
        public FruitDynamicProxy(Fruit fruit) {
            this.fruit = fruit;
        }
    
        /**
         * 获取代理对象实例
         * newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法
         *
         * @return
         */
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(
                    //代理对象的类加载器
                    fruit.getClass().getClassLoader(),
                    //代理对象需要实现的接口,可以是多个
                    fruit.getClass().getInterfaces(),
                    //方法调用的实际对象,
                    this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("FruitDynamicProxy方法名 " + method.getName() + " ---参数 " + Arrays.toString(args));
    
            //代理对象调用相应方法的返回结果
            Object invoke = method.invoke(fruit, args);
    
            return invoke;
        }
    }

    测试:

    FruitDynamicProxy fruitDynamicProxy = new FruitDynamicProxy(new Apple());
    Fruit banana = (Fruit) fruitDynamicProxy.getProxyInstance();
    //调用该方法时转到FruitDynamicProxy类的invoke方法
    banana.name("apple");
    banana.price(12.6);

    2.2 说明

    实现接口InvocationHandler类,代理对象在调用方法时就会转到该类的invoke()方法。之后在获取代理对象时使用Proxy的newProxyInstance()方法,该方法会返回一个实现具体接口的代理对象,该代理对象的所有方法调用都会转发到InvocationHandler.invoke()方法,具体是使用了java反射机制

    newProxyInstance()方法的三个参数:

    1. loader,指定代理对象的类加载器;
    2. interfaces,代理对象需要实现的接口,可以同时指定多个接口;
    3. handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里。

    3、CGLIB动态代理

    3.1 代码示例

    代理对象类:

    public class CglibFruit {
    
        public String name(String name) {
            System.out.println("this is " + name);
            return name;
        }
    
        public double price(double price) {
            System.out.println("price is " + price);
            return price;
        }
    
        //final修饰的方法非子类无法访问
        public final void hello(String msg) {
            System.out.println("final method " + msg);
        }
    }

    实现MethodInterceptor类:

    public class CglibMethodInterceptor implements MethodInterceptor {
    
        private CglibFruit fruit;
    
        public CglibMethodInterceptor(CglibFruit fruit) {
            this.fruit = fruit;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
            System.out.println("CglibMethodInterceptor method " + method.getName() + " --- 参数 " + objects);
    
            Object o1 = methodProxy.invokeSuper(o, objects);
    
            return o1;
        }
    
        /**
         * 获取代理对象实例,通过Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最后通过调用create()方法得到代理对象   
         * @return
         */
        public Object getProxyInstance() {
            Enhancer enhancer = new Enhancer();
            //设置代理对象class字节
            enhancer.setSuperclass(this.fruit.getClass());
            //设置回调方法
            enhancer.setCallback(this);
            //创建代理对象
            Object o = enhancer.create();
            return o;
        }
    }

    测试:

    CglibMethodInterceptor cmi = new CglibMethodInterceptor(new CglibFruit());
    CglibFruit fruit = (CglibFruit) cmi.getProxyInstance();
    fruit.name("orange");
    fruit.price(12.666);

    3.2 说明

    实现MethodInterceptor类,代理对象的方法调用会被转发到该类的intercept()方法。在获取代理对象时通过Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最后通过调用create()方法得到代理对象。对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法。

    4、两者区别

      java动态代理是领用反射机制生成一个代理接口匿名类,在调用具体的方法前调用InvocationHandler.invoke()方法来处理。CGLIB动态代理是对代理对象类的class文件加载进来,通过修改器字节码成成子类来处理。

    两者使用:

      如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

      如果目标对象实现了接口,可以强制使用CGLIB实现AOP

      如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    参照原码 Github

  • 相关阅读:
    iOS- static extern const
    App 性能分析
    迭代器和生成器
    软件工程之个人软件开发----起步
    myeclipse调式与属性显示
    hdu 6188
    uva10780 质因子分解
    云服务器端口号的几个操作
    Redis错误 : MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk.
    ubuntu系统安装宝塔面板Linux版
  • 原文地址:https://www.cnblogs.com/kingsonfu/p/10633687.html
Copyright © 2011-2022 走看看