学习笔记Spring中的动态代理

Posted 棉花糖灬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记Spring中的动态代理相关的知识,希望对你有一定的参考价值。

本文参考了文章Spring AOP动态代理的实现方式和文章Spring中的动态代理

1. 动态代理的两种实现方式

(1) 两种动态代理

Spring中的AOP(面向切面编程)是基于动态代理技术实现的,而动态代理是基于反射设计的。动态代理的实现方式有2种:JDK动态代理和CGLIB动态代理。

  • JDK动态代理:通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口,核心是InvocationHandler接口和Proxy类
  • CGLIB(Code Generation Library)动态代理:CGLIB是一个代码生成类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的

(2) 注意事项

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

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

  • 如果目标对象没有实现接口,必须采用CGLIB的动态代理,spring会自动在两种模式之间转换

  • 不管哪一种方式都不能用private和final做修饰词

2. 动态代理实例

假设现在有一个addUser方法,它会往数据库中添加一条记录,现在要通过动态代理对其做增加,在该方法执行前后分别打印一条日志。本节只把两种动态代理共用的部分创建出来。

(1) 创建UserService接口类

public interface UserService 
    
    // 模拟添加用户的方法
    public void addUser();
    

(2) 创建UserServiceImpl接口实现类

public class UserServiceImpl implements UserService
    
    // 模拟添加用户的方法
    @Override
    public void addUser()
        System.out.println("模拟添加用户");
    

3. 基于JDK的动态代理

(1) 创建JDK动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyProxy 
    public static UserService getJDKProxy(UserService service) 
        /*
        * Proxy.newProxyInstance的参数有三个:
        *   目标对象的类加载器
        *   目标对象的接口
        *   代理对象的执行处理器
        * */
        UserService userService = (UserService)Proxy.newProxyInstance(service.getClass().getClassLoader(),
            service.getClass().getInterfaces(),
            new InvocationHandler() 
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
                    System.out.println("addUser()方法执行前打印日志");
                    // 调用原方法
                    Object result = method.invoke(service, args);
                    System.out.println("addUser()方法执行后打印日志");
                    return result;
                
            );
        return userService;
    

(2) 编写测试方法

import org.junit.jupiter.api.Test;

public class ProxyTest 

    @Test
    public void JDKProxyTest()
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        // 生成代理对象
        UserService proxy = MyProxy.getJDKProxy(userService);
        // 调用目标对象方法
        userService.addUser();
        System.out.println("=======================================");
        // 调用代理对象方法
        proxy.addUser();
    

(3) 运行结果

模拟添加用户
=======================================
addUser()方法执行前打印日志
模拟添加用户
addUser()方法执行后打印日志

4. 基于CGLIB的动态代理

(1) 创建CGLIB动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.apache.catalina.User;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class MyProxy 

    public static UserService getGCLIBProxy(UserService service) 
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        // 设置需要增强的类的对象
        enhancer.setSuperclass(service.getClass());
        // 设置回调方法
        enhancer.setCallback(new MethodInterceptor() 
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
                throws Throwable 
                System.out.println("addUser()方法执行前打印日志");
                // 调用原方法
                Object result = methodProxy.invokeSuper(o, args);
                System.out.println("addUser()方法执行后打印日志");
                return result;
            
        );
        UserService userService = (UserService)enhancer.create();
        return userService;
    

(2) 编写测试方法

import org.junit.jupiter.api.Test;

public class ProxyTest 

    @Test
    public void CGLIBProxyTest()
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        // 生成代理对象      
        UserService proxy = MyProxy.getGCLIBProxy(userService);
        // 调用目标对象方法
        userService.addUser();
        System.out.println("=======================================");
        // 调用代理对象方法
        proxy.addUser();
    

其输出和JDKProxy的输出相同

以上是关于学习笔记Spring中的动态代理的主要内容,如果未能解决你的问题,请参考以下文章

Spring5学习笔记 — “Spring AOP底层原理(动态代理)”

Spring5学习笔记 — “Spring AOP底层原理(动态代理)”

Spring学习笔记7:静态动态代理模式

Spring学习笔记③

Spring5 AOP 学习笔记

spring学习笔记四:AOP