跳至主要內容

1.2 Spring AOP原理


Spring AOP(面向切面编程)的实现原理基于代理模式和动态代理技术,主要利用 JDK 动态代理和 CGLIB 动态代理两种方式来实现切面的织入。

  1. JDK 动态代理
    JDK 动态代理是基于接口的代理,在运行时创建接口的代理类实例。Spring AOP 使用 JDK 动态代理来为实现了接口的目标对象创建代理。主要步骤包括:

    • 定义一个切面类,其中包含切面逻辑(如日志、事务管理等)。
    • 获取目标对象的接口,创建一个代理类,该代理类实现了目标对象的接口,并在方法调用时调用切面逻辑。
    • 将切面逻辑与目标对象的方法调用进行委托,实现切面的织入。
  2. CGLIB 动态代理
    CGLIB(Code Generation Library)是一个基于字节码生成的代码生成库,它可以为没有实现接口的类创建子类,从而实现动态代理。Spring AOP 使用 CGLIB 动态代理来为没有实现接口的目标对象创建代理。主要步骤包括:

    • 定义一个切面类,其中包含切面逻辑。
    • 获取目标对象的类信息,创建一个代理类,该代理类继承了目标对象的类,并在目标方法上添加切面逻辑。
    • 在代理类中重写目标方法,将切面逻辑与目标方法调用进行融合,实现切面的织入。

Spring AOP 根据目标对象是否实现了接口来决定使用 JDK 动态代理还是 CGLIB 动态代理。如果目标对象实现了接口,则使用 JDK 动态代理;如果目标对象没有实现接口,则使用 CGLIB 动态代理。这样,Spring AOP 可以实现对目标对象的方法进行增强,而不修改原始代码,实现了横切关注点的织入,提高了代码的可维护性和灵活性。

下面是一个简单的 Spring AOP 示例代码,演示了如何使用 Spring AOP 来记录方法的执行时间:

假设有一个服务类 UserService,其中有一个方法 performAction()

public class UserService {
    public void performAction() {
        // 模拟执行一个耗时的操作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Performing action in UserService");
    }
}

现在,我们希望记录 performAction() 方法的执行时间。我们可以使用 Spring AOP 来实现这个功能。

首先,定义一个切面类 PerformanceAspect

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PerformanceAspect {

    @Around("execution(* UserService.performAction())")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.nanoTime();

        // 执行被通知的方法
        Object result = joinPoint.proceed();

        long endTime = System.nanoTime();
        long executionTime = endTime - startTime;

        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + " nanoseconds");

        return result;
    }
}

在上述代码中,我们定义了一个切面类 PerformanceAspect,并在其内部定义了一个环绕通知方法 measureExecutionTime()。该方法通过 @Around 注解表示是一个环绕通知,它拦截了 UserService 类中的 performAction() 方法的执行。

然后,我们需要在 Spring 配置文件中启用 AOP,并扫描切面类的包。

假设我们的 Spring 配置文件为 applicationContext.xml,其中包含以下内容:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 开启注解驱动 -->
    <aop:aspectj-autoproxy />

    <!-- 扫描切面类所在的包 -->
    <context:component-scan base-package="com.example.aspect" />

</beans>

在这个配置文件中,我们使用了 <aop:aspectj-autoproxy /> 来启用注解驱动的 AOP。并且通过 <context:component-scan> 来扫描切面类所在的包。现在,当调用 UserServiceperformAction() 方法时,切面 PerformanceAspect 将会拦截并记录方法的执行时间。

上次编辑于: