AOP 面向切面编程
2026/1/15大约 3 分钟
AOP 面向切面编程
什么是 AOP
AOP(Aspect-Oriented Programming)面向切面编程,将横切关注点(如日志、事务、权限)从业务逻辑中分离出来。
AOP 核心概念
| 概念 | 说明 |
|---|---|
| Aspect(切面) | 横切关注点的模块化 |
| Join Point(连接点) | 程序执行的某个点(方法调用、异常抛出) |
| Pointcut(切点) | 匹配连接点的表达式 |
| Advice(通知) | 在切点执行的动作 |
| Target(目标对象) | 被代理的对象 |
| Proxy(代理对象) | AOP 创建的代理 |
| Weaving(织入) | 将切面应用到目标对象的过程 |
通知类型
| 类型 | 说明 |
|---|---|
| @Before | 前置通知,方法执行前 |
| @After | 后置通知,方法执行后(无论是否异常) |
| @AfterReturning | 返回通知,方法正常返回后 |
| @AfterThrowing | 异常通知,方法抛出异常后 |
| @Around | 环绕通知,包围方法执行 |
执行顺序
切面示例
@Aspect
@Component
public class LogAspect {
// 切点表达式
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {}
// 前置通知
@Before("servicePointcut()")
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("方法: " + methodName + ", 参数: " + Arrays.toString(args));
}
// 返回通知
@AfterReturning(pointcut = "servicePointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("返回值: " + result);
}
// 异常通知
@AfterThrowing(pointcut = "servicePointcut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("异常: " + ex.getMessage());
}
// 后置通知
@After("servicePointcut()")
public void after(JoinPoint joinPoint) {
System.out.println("方法执行完毕");
}
// 环绕通知
@Around("servicePointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long time = System.currentTimeMillis() - start;
System.out.println("执行时间: " + time + "ms");
}
}
}切点表达式
execution 表达式
// 语法
execution(修饰符? 返回类型 包名.类名.方法名(参数) 异常?)
// 示例
// 匹配 UserService 的所有方法
execution(* com.example.service.UserService.*(..))
// 匹配 service 包下所有类的所有方法
execution(* com.example.service.*.*(..))
// 匹配 service 包及子包下所有类的所有方法
execution(* com.example.service..*.*(..))
// 匹配所有 public 方法
execution(public * *(..))
// 匹配返回 String 的方法
execution(String *(..))
// 匹配第一个参数为 String 的方法
execution(* *(String, ..))其他切点表达式
// 匹配指定注解的方法
@annotation(com.example.annotation.Log)
// 匹配指定注解的类
@within(org.springframework.stereotype.Service)
// 匹配指定类型
within(com.example.service.*)
// 匹配 Bean 名称
bean(userService)
bean(*Service)
// 组合表达式
@Pointcut("execution(* com.example.service.*.*(..)) && @annotation(com.example.annotation.Log)")
public void logPointcut() {}代理模式
JDK 动态代理
基于接口,目标类必须实现接口。
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("保存用户");
}
}
// JDK 动态代理
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
(proxy1, method, args) -> {
System.out.println("前置处理");
Object result = method.invoke(target, args);
System.out.println("后置处理");
return result;
}
);CGLIB 代理
基于继承,不需要接口。
public class UserService {
public void save() {
System.out.println("保存用户");
}
}
// CGLIB 代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("前置处理");
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置处理");
return result;
});
UserService proxy = (UserService) enhancer.create();Spring AOP 代理选择
| 条件 | 代理方式 |
|---|---|
| 目标类实现接口 | JDK 动态代理(默认) |
| 目标类未实现接口 | CGLIB 代理 |
| 强制使用 CGLIB | @EnableAspectJAutoProxy(proxyTargetClass = true) |
自调用问题
同一个类中方法调用不会触发 AOP。
@Service
public class UserService {
@Transactional
public void methodA() {
// 直接调用,不会触发 AOP
methodB();
}
@Transactional
public void methodB() {
// ...
}
}解决方案
@Service
public class UserService {
@Autowired
private UserService self; // 注入自己
public void methodA() {
self.methodB(); // 通过代理调用
}
@Transactional
public void methodB() {
// ...
}
}
// 或者使用 AopContext
@Service
public class UserService {
public void methodA() {
((UserService) AopContext.currentProxy()).methodB();
}
@Transactional
public void methodB() {
// ...
}
}多切面执行顺序
@Aspect
@Order(1) // 数字越小,优先级越高
@Component
public class FirstAspect {
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("First Before");
Object result = pjp.proceed();
System.out.println("First After");
return result;
}
}
@Aspect
@Order(2)
@Component
public class SecondAspect {
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Second Before");
Object result = pjp.proceed();
System.out.println("Second After");
return result;
}
}
// 执行顺序:
// First Before -> Second Before -> 目标方法 -> Second After -> First After常见应用场景
- 日志记录
- 性能监控
- 事务管理
- 权限校验
- 缓存处理
- 异常处理