1.2 Spring AOP原理
Spring AOP(面向切面编程)的实现原理基于代理模式和动态代理技术,主要利用 JDK 动态代理和 CGLIB 动态代理两种方式来实现切面的织入。
JDK 动态代理:
JDK 动态代理是基于接口的代理,在运行时创建接口的代理类实例。Spring AOP 使用 JDK 动态代理来为实现了接口的目标对象创建代理。主要步骤包括:- 定义一个切面类,其中包含切面逻辑(如日志、事务管理等)。
- 获取目标对象的接口,创建一个代理类,该代理类实现了目标对象的接口,并在方法调用时调用切面逻辑。
- 将切面逻辑与目标对象的方法调用进行委托,实现切面的织入。
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>
来扫描切面类所在的包。现在,当调用 UserService
的 performAction()
方法时,切面 PerformanceAspect
将会拦截并记录方法的执行时间。