详解 Spring 框架中的动态代理(附案例)

发布于:2024-06-29 ⋅ 阅读:(10) ⋅ 点赞:(0)

涉及到 Spring 框架中的动态代理时,主要是指 Spring AOP(面向切面编程)中的代理机制。Spring AOP 使用动态代理来实现切面功能,通过在运行时动态地创建代理对象来增强原始对象的行为。

Spring 动态代理概述

  1. 代理模式简介
    • 代理模式是一种结构型设计模式,允许在不改变原始类(被代理类)代码的情况下,通过引入代理类来控制对原始类的访问。
  2. Spring 中的代理机制
    • Spring AOP 提供两种代理方式:基于接口的 JDK 动态代理和基于类的 CGLIB 动态代理。–默认的实现是 JDK 动态代理
  3. JDK 动态代理
    • 如果目标对象实现了接口,Spring AOP 将使用 JDK 动态代理来创建 AOP 代理。在运行时,Spring AOP 使用 Java 标准库 java.lang.reflect.Proxy 来创建代理对象,该代理对象实现了目标对象的所有接口,并在方法调用前后执行切面逻辑。
javapublic interface UserService {
    void saveUser(User user);
}
javapublic class UserServiceImpl implements UserService {
    public void saveUser(User user) {
        System.out.println("我是impl");
    }
}
/**
 * MyAspect 类实现了 MethodInterceptor 接口,用于实现面向切面编程(AOP)的切面逻辑。
 * 该类的主要作用是在目标方法执行前、执行后或出现异常时注入自定义的处理逻辑。
 */
public class MyAspect implements MethodInterceptor {

    /**
     * 方法拦截器的核心方法,用于拦截目标对象的方法调用。
     *
     * @param invocation 方法调用的拦截对象,包含目标对象、目标方法等信息。
     * @return 返回目标方法的执行结果,可以根据需要进行处理或修改。
     * @throws Throwable 如果目标方法执行过程中抛出异常,该方法可以重新抛出异常或进行异常处理。
     */
    @Nullable
    @Override
    public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {

        System.out.println("我是MyAspect");
        System.out.println("--------------------");
        // 调用 invocation.proceed() 来执行目标方法,这是 AOP 中心点调用目标方法的入口。
        // 在此之前或之后可以添加额外的逻辑,例如日志记录、性能监控、权限检查等。
        // 在目标方法执行前后添加额外逻辑
        Object result = invocation.proceed();
        return result;
    }


    public static void main(String[] args) {
        // 创建一个代理工厂对象
        // 创建 AOP 代理
        ProxyFactory proxyFactory = new ProxyFactory();
        // 设置代理的目标对象为一个实现了UserService接口的具体服务实现
        proxyFactory.setTarget(new UserServiceImpl());
        // 添加一个切面建议,该切面将被织入到目标对象的方法调用中
        proxyFactory.addAdvice(new MyAspect());

        // 通过代理工厂获取代理对象,该代理对象实现了UserService接口
        // 在调用其方法时,会先执行MyAspect中的逻辑
        UserService proxy = (UserService) proxyFactory.getProxy();
        proxy.saveUser(new User());
    }
}

QQ截图20240624110328.png执行结果显示,在调用saveUser之前,执行了invoke方法中的逻辑

  1. CGLIB 动态代理:
  • 如果目标对象没有实现接口,Spring AOP 将使用 CGLIB 动态代理。CGLIB(Code Generation Library)通过继承目标类来创建代理对象,因此可以对类的方法进行增强。
public class UserService {
    public void saveUser(String username) {
        System.out.println("Saving user: " + username);
    }
}
/**
 * 日志切面类,用于实现方法执行前后的日志记录。
 * 通过实现MethodInterceptor接口,可以在方法执行前、执行后和发生异常时插入自定义逻辑。
 */
public class LogAspect implements MethodInterceptor {
    /**
     * 在目标方法执行前后的拦截器方法。
     *
     * @param o       目标对象,即被拦截的对象。
     * @param method  目标方法,即被拦截的方法。
     * @param objects 方法参数。
     * @param proxy   方法代理,用于调用目标方法。
     * @return 目标方法的返回值。
     * @throws Throwable 如果目标方法执行过程中抛出异常。
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
        System.out.println("执行目标方法之前");
        Object result = proxy.invokeSuper(o, objects);
        System.out.println("执行目标方法之后");
        return result;
    }

    public static void main(String[] args) {
        // 创建Enhancer对象,用于生成动态代理类
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        // 设置代理类的超类
        enhancer.setSuperclass(UserService.class);
        // 设置回调函数,即拦截器
        enhancer.setCallback(new LogAspect());
        // 创建动态代理类实例
        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();

        // 调用代理对象的方法,此时会触发拦截器中的intercept方法
        // 调用代理对象的方法
        proxy.saveUser("Alice");
    }
}

QQ截图20240624135517.png

  1. 代理选择
    • Spring AOP 默认情况下优先使用 JDK 动态代理,只有当目标类没有实现接口时才会使用 CGLIB 动态代理。可以通过配置强制使用 CGLIB 或者禁用 CGLIB 来控制代理方式。
  2. 应用场景
    • 动态代理在 Spring 中广泛应用于事务管理、日志记录、性能监控等方面,通过声明式的 AOP 配置将切面逻辑与业务逻辑分离,提高了代码的可维护性和可测试性。

网站公告

今日签到

点亮在社区的每一天
去签到