Talking about JDK dynamic proxy and CGLIB dynamic proxy in Spring

What is proxy mode

​ Proxy Pattern (Proxy Pattern) provides a proxy for an object, and the proxy object controls the reference of the original object, and the proxy object plays an intermediate role between the client and the target object.

​ Proxy mode is one of the commonly used structural design modes. When there is a problem with direct access to some objects, it can be accessed indirectly through a proxy object. In order to ensure the transparency of client use, the real objects accessed need to implement the same interface.

The proxy mode can be divided into two types: static proxy and dynamic proxy, and dynamic proxy is divided into JDK dynamic proxy and CGLIB proxy. Today we mainly talk about dynamic agents.

Definition and Application of Dynamic Proxy

​Dynamic proxy: It is a technology that uses reflection and bytecode to create a subclass of a specified interface or class and its instance object at runtime. Through this technology, the code can be enhanced without intrusion.

​ Dynamic proxies are widely used, and they can be seen in various open source frameworks. For example, aop in spring is enhanced with dynamic proxies, and dynamic proxies are used in mybatis to generate mappers.

Dynamic proxy implemented by JDK

analyze

Use the interceptor (must implement the InvocationHandler interface) plus the reflection mechanism to generate an anonymous class of the proxy interface, and call InvokeHandler to process it before calling the specific method

The dynamic proxy implemented by jdk consists of two important members, namely Proxy,InvocationHandler

  • Proxy: is the parent class of all dynamic proxies, which provides a static method to create class objects and instances of dynamic proxies

  • InvocationHandler: Each dynamic proxy instance has an associated invoke method that the method call will be InvocationHandlerforwarded to when calling a method on the proxy instanceInvocationHandler

1. Code implementation

IStudentService interface


/**
 * @Author charles.yao
 * @Description 抽象角色,一般都是用接口,或者抽象类解决。
 * @Date 2022/10/21 15:32
 */
public interface IStudentService {
    /**
     * 增加
     */
    public void addStudent();

    /**
     * 删除
     */
    public void deleteStudent();

    /**
     * 更新
     */
    public void updateStudent();

    /**
     * 查询
     */
    public void selectStudent();
}

Implement class StudentServiceImpl


/**
 * @Author charles.yao
 * @Description 学生信息service
 * @Date 2022/10/21 15:32
 */
@Slf4j
@Service
public class StudentServiceImpl implements IStudentService {
    @Override
    public void addStudent() {
        log.info("执行了addStudent");
    }

    @Override
    public void deleteStudent() {
        log.info("执行了deleteStudent");
    }

    @Override
    public void updateStudent() {
        log.info("执行了updateStudent");

    }

    @Override
    public void selectStudent() {
        log.info("执行了selectStudent");
    }
}

ProxyInvocationHandler proxy class, implementing the InvocationHandler interface to rewrite the invoke method


/**
 * @Author charles.yao
 * @Description 实现InvocationHandler接口重写invoke方法, 自动生成一个代理类
 * @Date 2022/10/21 15:35
 */
@Slf4j
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理得目标接口
    private Object target;

    public ProxyInvocationHandler() {
    }

    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }
    /**
     * 生成需要得动态代理类
     *
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    /**
     * 处理代理的实例,并返回结果
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("方法:{}被执行", method.getName());
        //TODO 执行方法前需要增强得部分

        log.info("执行方法前,需要对学生信息进行校验>>>>>>");
        Object invoke = method.invoke(target, args);
        log.info("执行方法结束>>>>>>");
        return invoke;
    }
}

Test Methods


/**
 * @Author charles.yao
 * @Description jdk动态代理测试
 * @Date 2022/10/21 15:44
 */
public class JdkProxyTest {
    public static void main(String[] args) {
        //真实类
        StudentServiceImpl studentService = new StudentServiceImpl();

        //代理角色,不存在的,
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        //设置要代理得对象
        proxyInvocationHandler.setTarget(studentService);
        //动态生成代理类
        IStudentService iStudentService = (IStudentService) proxyInvocationHandler.getProxy();

        iStudentService.addStudent();
    }

Test Results

16:02:38.053 [main] INFO com.test.ProxyInvocationHandler - 方法:addStudent被执行
16:02:38.064 [main] INFO com.test.ProxyInvocationHandler - 执行方法前,需要对学生信息进行校验>>>>>>
16:02:38.064 [main] INFO com.test.StudentServiceImpl - 执行了addStudent
16:02:38.066 [main] INFO com.test.ProxyInvocationHandler - 执行方法结束>>>>>>

CGlib dynamic proxy

1. Analysis

CGLIB(Code Generation Library)It is an ASM-based bytecode generation library that allows us to modify and dynamically generate bytecode at runtime. CGLIB implements proxy through inheritance (in the last part, let's think about what limitations it may have. The final method cannot be rewritten, so it cannot enhance the final modified method. Let's verify this later)

The implementation of CGLIB also has two important members, Enhancer, MethodInterceptor, in fact, the use of these two is very similar to the dynamic agent implemented Proxyby InvocationHandlerjdk

  • Enhancer: To specify the target object to be proxied, the object that actually processes the proxy logic, and finally create()get the proxy object by calling the method, and all non-final method calls to this object will be forwarded toMethodInterceptor
  • MethodInterceptor: The method call of the dynamic proxy object will be forwarded to interceptthe method for enhancement

2. Code example

CGlib is not like a JDK dynamic agent. CGlib needs to import Jar packages, so I use SpringBoot to directly import dependencies

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>

StudentServiceImpl得类


/**
 * @Author charles.yao
 * @Description 学生信息service
 * @Date 2022/10/21 15:32
 */
@Slf4j
@Service
public class StudentServiceImpl implements IStudentService {
    @Override
    public void addStudent() {
        log.info("执行了addStudent");
    }

    @Override
    public void deleteStudent() {
        log.info("执行了deleteStudent");
    }

    @Override
    public void updateStudent() {
        log.info("执行了updateStudent");

    }

    @Override
    public void selectStudent() {
        log.info("执行了selectStudent");
    }
}

Proxy class StudentServiceCGlib


/**
 * @Author charles.yao
 * @Description CGlib 实现servce
 * @Date 2022/10/21 16:21
 */
@Slf4j
public class StudentServiceCGlib implements MethodInterceptor {
    //需要代理得类
    private Object target;

    public StudentServiceCGlib() {
    }

    public StudentServiceCGlib(Object target) {
        this.target = target;
    }

    /**
     * 返回一个代理对象,是target对象得代理对象     *
     * 利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
     *
     * @return
     */
    public Object getProxyInstance() {
        //创建一个cglib工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建子类对象,就是代理对象
        return enhancer.create();


    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        log.info("开始对代理对象增强啦>>>>>>>>>>");
        //TODO 添加业务增强代码
        Object result = methodProxy.invokeSuper(o, objects);
        log.info("对代理对象增强结束啦>>>>>>>>>>");
        return result;
    }
}

Test class CGlibProxyTest

/**
 * @Author charles.yao
 * @Description CGlib 动态搭理测试类
 * @Date 2022/10/21 16:28
 */
public class CGlibProxyTest {
    public static void main(String[] args) {

        StudentServiceCGlib studentServiceCGlib = new StudentServiceCGlib(new StudentServiceImpl());
        //利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
        StudentServiceImpl proxyInstance = (StudentServiceImpl) studentServiceCGlib.getProxyInstance();

        proxyInstance.addStudent();

        proxyInstance.selectStudent();
    }
}

Results of the:

16:29:50.953 [main] INFO com.server.test.StudentServiceCGlib - 开始对代理对象增强啦>>>>>>>>>>
16:29:50.985 [main] INFO com.server.test.StudentServiceImpl - 执行了addStudent
16:29:50.985 [main] INFO com.server.test.StudentServiceCGlib - 对代理对象增强结束啦>>>>>>>>>>
16:29:50.985 [main] INFO com.server.test.StudentServiceCGlib - 开始对代理对象增强啦>>>>>>>>>>
16:29:50.985 [main] INFO com.server.test.StudentServiceImpl - 执行了selectStudent
16:29:50.985 [main] INFO com.server.test.StudentServiceCGlib - 对代理对象增强结束啦>>>>>>>>>>

final summary

After jdk6, jdk7, and jdk8 gradually optimize the JDK dynamic proxy, the efficiency of the JDK proxy is higher than that of the CGLIB proxy when the number of calls is small. Only when a large number of calls are made, jdk6 and jdk7 are less efficient than the CGLIB proxy. But when it comes to jdk8, the JDK proxy is more efficient than the CGLIB proxy.

1. If the target object implements the interface, JDK's dynamic proxy will be used by default.
2. If the target object implements the interface, CGLIB can also be used forcibly.
3. If the target object does not implement the interface, the CGLIB library must be used, and spring will automatically Convert between JDK dynamic proxies and CGLIB

​Follow
my WeChat public
accountinsert image description here

Guess you like

Origin blog.csdn.net/CharlesYooSky/article/details/127449424