【学习笔记】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,动态代理,proxy)