一、什么是AOP
Spring的AOP(面向切面编程,Aspect-Oriented Programming)是一种编程范式,它允许你将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。横切关注点是指那些影响多个类或模块的功能,如日志记录、事务管理、权限检查等。通过AOP,这些功能可以被集中管理和处理,而无需在每个业务逻辑方法中重复编写代码。
二、AOP的核心概念
1、切面(Aspect)
切面是包含横切逻辑的组件。它可以是一个类,其中定义了通知(Advice)和切入点(Pointcut)。切面将横切关注点模块化为可重用的组件。
2、通知(Advice)
通知是在特定连接点执行的代码。它定义了在何处以及如何执行横切逻辑。Spring AOP支持五种类型的通知:
- Before:前置通知,在方法调用之前执行。
- After:后置通知,在方法调用之后执行,无论方法是否抛出异常。
- After-returning:返回通知,仅在方法成功返回后执行。
- After-throwing:异常通知,仅在方法抛出异常时执行。
- Around:环绕通知,在方法调用前后都可以执行自定义逻辑。
3、切入点(Pointcut)
切入点定义了通知应该应用到哪些连接点。它是一组匹配的方法或类的规则,通常使用表达式来指定。例如,execution(* com.example.service.*.*(..))表示匹配com.example.service包下的所有方法。
4、连接点(Join Point)
连接点是程序执行过程中的某个点,比如方法调用、异常抛出等。在Spring AOP中,连接点通常是方法调用。
5、引入(Introduction)
引入允许你向现有类添加新的方法或属性。虽然不常用,但在某些情况下可以增强现有类的功能。
6、织入(Weaving)
织入是将切面应用到目标对象的过程。织入可以在编译时、类加载时或运行时进行。Spring AOP采用的是运行时织入,即通过代理机制在运行时动态地将切面应用到目标对象。
三、Spring AOP的工作原理
Spring AOP基于代理模式实现。对于Spring管理的Bean,默认使用JDK动态代理(如果接口存在)或CGLIB代理(如果不存在接口)。代理对象会拦截对目标对象方法的调用,并在适当的时候插入横切逻辑。
AOP适用于以下场景:
1、日志记录:在方法调用前后记录日志。
2、事务管理:在服务层方法上自动管理事务。
3、权限检查:在访问敏感资源前进行权限验证。
4、性能监控:统计方法执行时间。
5、缓存管理:在方法调用前后控制缓存的读取和写入。
通过AOP,开发者可以更清晰地分离关注点,使代码更加模块化和易于维护。
四、配置Spring AOP的切面
在Spring框架中,配置AOP切面主要可以通过注解驱动和XML配置两种方式实现。
1、注解驱动配置(推荐方式)
(1)启用AOP自动代理
在Spring Boot或Java配置类中添加注解启用自动代理:
@Configuration
@EnableAspectJAutoProxy // 启用AOP自动代理
public class AppConfig {
}
(2)定义切面类
使用@Aspect
注解标记切面类,并通过@Component
使其被Spring管理:
@Aspect
@Component
public class LoggingAspect {
// 定义切点:匹配com.example.service包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 前置通知
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("方法调用前:" + joinPoint.getSignature().getName());
}
// 环绕通知(可控制是否执行原方法)
@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知-方法开始");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕通知-方法结束");
return result;
}
}
(3)切点表达式说明
execution(* com.service.*.*(..))
: 匹配com.service
包下的所有方法@annotation(com.example.Loggable)
: 匹配带有@Loggable
注解的方法
2、XML配置方式(传统配置)
(1)启用AOP命名空间
在applicationContext.xml
中添加AOP命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
(2)配置切面
<!-- 定义切面Bean -->
<bean id="loggingAspect" class="com.example.aspect.LoggingAspect"/>
<!-- AOP配置 -->
<aop:config>
<aop:aspect ref="loggingAspect">
<!-- 定义切点 -->
<aop:pointcut id="serviceMethods"
expression="execution(* com.example.service.*.*(..))"/>
<!-- 前置通知 -->
<aop:before method="logBefore" pointcut-ref="serviceMethods"/>
<!-- 环绕通知 -->
<aop:around method="logAround" pointcut-ref="serviceMethods"/>
</aop:aspect>
</aop:config>
3、进阶配置技巧
(1)组合切点表达式
@Pointcut("execution(* com.service.*.*(..)) && args(param)")
public void serviceMethodsWithParam(String param) {}
@Before("serviceMethodsWithParam(param)")
public void logParam(String param) {
System.out.println("参数值:" + param);
}
(2)使用自定义注解标记切点
定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {}
切面配置:
@After("@annotation(com.example.Auditable)")
public void auditAction() {
System.out.println("执行XXX操作");
}
4、配置方式对比
配置方式 | 优点 | 缺点 |
---|---|---|
注解驱动 | 简洁直观,与代码紧密结合 | 切面逻辑分散在多个类中 |
XML配置 | 集中管理切面,便于统一修改 | 配置冗余,可读性较低 |
实际开发中推荐注解驱动方式,特别是在Spring Boot项目中默认支持自动代理,XML配置更适合需要集中管理AOP规则的传统项目。