动态代理的实现方式,及JDKPorxy 和CGLib的区别

100 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

动态代理常用的实现方式是发射. 但动态代理不止有反射这一种实现方式.

eg: 动态代理也可以是通过CGLib来实现,而CGLib是基于ASM(一个java字节码操作框架),而实现的.

简单说: 动态代理是一种行为方式,而反射或ASM只是它的一种实现手段而已

JDKPorxy 和CGLib区别:

  1. JDK Proxy 是java语言自带的功能,无需加载三方 jar来实现.
  2. java对 JDK Proxy 提供了稳定的支持,并且会持续提升和更新JDK Proxy,   eg: java 8 中JDK Proxy 性能相比于之前版本提升了很多
  3.  JDK Proxy 是通过拦截器加 反射的方式实现的
  4.  JDK Proxy只能代理集成接口的类
  5.  JDK Proxy 实现和调用起来比较简单
  6.  CGLib 是第三方提供的工具,基于ASM实现的,性能比较高
  7.  CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的
public class JDKProxyExample {

    static interface  Car {
        void run();
    }

    static  class  Bus implements Car{

        @Override
        public void run() {
            System.out.println("Bus running");
        }
    }

    static  class  Taxi implements Car{

        @Override
        public void run() {
            System.out.println("Taxi running");
        }
    }
    // 实现 InvocationHandler  接口
    static  class  JDKProxy implements InvocationHandler {
        // 代理对象
        private Object target;
        // 获取代理对象实例
        public Object getInstance(Object target) {
            this.target = target;
           // 获得代理的对象
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),JDKProxy.this);
        }
        /**
         * 执行代理方法
         * @param proxy  代理对象
         * @param method 代理方法 
         * @param args   方法参数  
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("动态代理之前的业务处理");
            Object invoke = method.invoke(target, args);
            return invoke;
        }
    }

    public static void main(String[] args) {
          JDKProxy jdkProxy = new JDKProxy();
          Car instance = (Car) jdkProxy.getInstance(new Taxi());
          instance.run();
          Car instance2 = (Car)jdkProxy.getInstance(new Bus());
          instance2.run();
    }
}

 依赖:

           <!-- https://mvnrepository.com/artifact/cglib/cglib -->
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author xulihui
 * @date 2020/4/7 13:10
 */
public class CGLibExample {

    static class Car {

        public void run() {
            System.out.println("Car running");
        }
    }

    static  class  CGLibProxy implements MethodInterceptor {
        // 接收代理对象
        private  Object target;

        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            // 设置父类为示例
            enhancer.setSuperclass(this.target.getClass());
            // 回调方法
            enhancer.setCallback(this);
            // 创建并返回代理对象
            return  enhancer.create();
        }


        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("方法调用前业务处理");
            // 执行调用方法
            Object invoke = methodProxy.invoke(o, objects);
            return invoke;
        }
    }

    public static void main(String[] args) {
        // 创建CGLib代理类
        CGLibProxy cgLibProxy = new CGLibProxy();
        // 初始化代理对象
        Car instance = (Car)cgLibProxy.getInstance(new Car());
        // 执行方法
        instance.run();
    }
}

spring框架中同时使用了两种动态代理 JDK Proxy 和CGLib,

当bean实现了接口时,Spring 就会使用CGLib

没有实现接口的时候,就会使用CGLib, 也可以在配置中强制使用CGLib, 只需要在Spring 配置中添加  <aop:aspectj-autoproxy proxy-target-class="true"/> 即可。