Spring AOP 是 Spring 框架提供的面向切面编程支持,用于将横切关注点(cross-cutting concerns,如日志、事务、权限检查等)与核心业务逻辑分离。AOP 允许开发者通过声明式方式将通用功能模块化,减少代码重复,提高代码可维护性。
核心概念
Spring AOP 基于代理模式实现,主要使用以下两种代理机制:
代理选择:
Spring AOP 提供两种主要配置方式:XML 配置 和 注解配置。以下分别讲解。
1. XML 配置方式
通过 XML 文件定义切面、切点和通知,适合需要集中管理配置的场景。
示例:日志切面
java
// 目标类
public class UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
// 切面类
public class LoggingAspect {
public void logBefore() {
System.out.println("Before method execution");
}
public void logAfter() {
System.out.println("After method execution");
}
public void logAfterReturning(Object returnValue) {
System.out.println("Method returned: " + returnValue);
}
public void logAfterThrowing(Throwable throwable) {
System.out.println("Exception occurred: " + throwable.getMessage());
}
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around before: " + joinPoint.getSignature());
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("Around after: " + joinPoint.getSignature());
return result;
}
}
XML 配置(spring-aop.xml)
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">
<bean id="userService" class="com.example.UserService"/>
<bean id="loggingAspect" class="com.example.LoggingAspect"/>
<aop:config>
<aop:aspect id="logAspect" ref="loggingAspect">
<aop:pointcut id="logPointcut" expression="execution(* com.example.UserService.*(..))"/>
<aop:before pointcut-ref="logPointcut" method="logBefore"/>
<aop:after pointcut-ref="logPointcut" method="logAfter"/>
<aop:after-returning pointcut-ref="logPointcut" method="logAfterReturning" returning="returnValue"/>
<aop:after-throwing pointcut-ref="logPointcut" method="logAfterThrowing" throwing="throwable"/>
<aop:around pointcut-ref="logPointcut" method="logAround"/>
aop:aspect>
aop:config>
beans>
测试
java
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
UserService userService = ctx.getBean("userService", UserService.class);
userService.addUser("Alice");
}
}
输出:
Around before: void com.example.UserService.addUser(String)
Before method execution
Adding user: Alice
Around after: void com.example.UserService.addUser(String)
After method execution
Method returned: null
2. 注解配置方式
通过 AspectJ 注解(如 @Aspect、@Pointcut**、@Before 等)定义切面,适合现代 Spring 应用。**
示例:日志切面(注解版)
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 定义切点
@Pointcut("execution(* com.example.UserService.*(..))")
public void logPointcut() {}
@Before("logPointcut()")
public void logBefore() {
System.out.println("Before method execution");
}
@After("logPointcut()")
public void logAfter() {
System.out.println("After method execution");
}
@AfterReturning(pointcut = "logPointcut()", returning = "returnValue")
public void logAfterReturning(Object returnValue) {
System.out.println("Method returned: " + returnValue);
}
@AfterThrowing(pointcut = "logPointcut()", throwing = "throwable")
public void logAfterThrowing(Throwable throwable) {
System.out.println("Exception occurred: " + throwable.getMessage());
}
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around before: " + joinPoint.getSignature());
Object result = joinPoint.proceed();
System.out.println("Around after: " + joinPoint.getSignature());
return result;
}
}
配置类
java
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy // 启用 AOP 注解支持
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
测试
java
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = ctx.getBean(UserService.class);
userService.addUser("Alice");
}
}
输出:与 XML 配置相同。
3. 切点表达式(Pointcut Expression)
Spring AOP 使用 AspectJ 切点表达式 来定义拦截规则,常用关键字:
特性 | Spring AOP | AspectJ |
---|---|---|
实现方式 | 运行时织入(基于代理) | 编译时/加载时/运行时织入 |
性能 | 稍低(运行时生成代理) | 较高(编译时优化) |
功能范围 | 仅支持方法级别的切点 | 支持方法、字段、构造器等多种切点 |
配置复杂度 | 简单(XML 或注解) | 较复杂(需要 AspectJ 编译器) |
依赖 | Spring 核心依赖 | 需要 AspectJ 库 |
选择建议:
注解方式需要添加 spring-aspects 依赖:
xml
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>${spring.version}version>
dependency>
八、总结
Spring AOP 是 Spring 框架提供的轻量级面向切面编程实现,通过代理模式(JDK 动态代理或 CGLIB)实现运行时织入。它通过切面、切点和通知分离横切关注点,广泛应用于日志、事务、权限控制等场景。Spring AOP 支持 XML 和注解配置,注解方式(@Aspect、@Pointcut 等)更现代化且易用。
核心要点: