作为Spring的几大核心之一,AOP是我们绕不开的一个重点。AOP也是Spring很优雅的一块内容,那到底AOP是什么呢?以及它的原理是什么呢?有很多问题我们要关注,那不如我们先从具体的场景出发来看看AOP解决了什么问题?

首先,AOP是解决了非业务代码入侵业务代码的问题,那怎么理解呢?我们平时在写各种方法的时候经常会碰到这种情况:
csharp
public class OrderService {
public void createOrder(Order order) {
// 非业务代码:日志记录
System.out.println("[LOG] 开始创建订单,订单ID: " + order.getId());
long startTime = System.currentTimeMillis();
try {
// 业务代码
validateOrder(order);
saveOrderToDB(order);
notifyWarehouse(order);
// 非业务代码:日志记录
System.out.println("[LOG] 订单创建成功,耗时: "
+ (System.currentTimeMillis() - startTime) + "ms");
} catch (Exception e) {
// 非业务代码:异常日志
System.err.println("[ERROR] 订单创建失败: " + e.getMessage());
throw e;
}
}
}
日志的代码散落在各个业务方法里,你如果想去改动日志的格式,那可就麻烦了。所以AOP闪亮登场:
java
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logMethodExecution(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
System.out.println("[LOG] 调用方法: " + methodName + ",参数: " + Arrays.toString(args));
long startTime = System.currentTimeMillis();
try {
Object result = pjp.proceed(); // 执行目标方法
System.out.println("[LOG] 方法执行成功,耗时: "
+ (System.currentTimeMillis() - startTime) + "ms");
return result;
} catch (Exception e) {
System.err.println("[ERROR] 方法执行失败: " + e.getMessage());
throw e;
}
}
}
// 业务代码纯净版
public class OrderService {
public void createOrder(Order order) {
validateOrder(order);
saveOrderToDB(order);
notifyWarehouse(order);
}
}
这样我们在修改日志代码的时候就无需这么复杂了,那这时候就有人该说了,就这点用? NONONONO,我们要知道AOP实际上最重要的是其思想,这个思想已经运用到我们经常用的一些地方了,比如事物管理、权限检验等......但我们只需要了解实际上AOP的重要思想就是 动态代理 。
1. Spring AOP的核心架构
Spring AOP的核心也是围绕着多个接口和类来实现的。
1.1 AopProxy
这个接口是顶级接口,定义了代理对象的获取方式。
csharp
public interface AopProxy {
Object getProxy();
}
实现类:JDK动态代理、CGLIB动态代理
1.2 Advised
这个接口是表示可被代理的对象,包含了代理的配置信息
csharp
public interface Advised {
TargetSource getTargetSource();
Advisor[] getAdvisors();
void addAdvisor(Advisor advisor);
...
}
实现类:ProxyFactory(也实现AopProxy)作用是整合代理配置信息然后再生成对应的代理对象
1.3 Advisor
通知器的接口,是Advice和Pointcut组合在一起的。
csharp
public interface Advisor {
Advice getAdvice();
boolean isPerInstance();
}
实现类:
◦ PointcutAdvisor:最常用的 Advisor 类型,包含 Pointcut 和 Advice
◦ AbstractPointcutAdvisor:PointcutAdvisor 的抽象实现
◦ DefaultPointcutAdvisor:最常用的具体实现
在具体的使用上,一般就是声明切面。这里很多小伙伴应该对通知的概念有些模糊,所谓的"通知"意思就是程序在执行特定的Join Point时应该采取的动作或行为。也是AOP框架在特定切入点插入的一些增强代码。
其他的接口例如,Advice和PointCut一个是通知接口一个是切点接口,这里Advice接口具体实现类就有我们经常说那几种通知,前置、后置、环绕等。
2. 代理生成的决策逻辑
我们要知道,Spring AOP是根据目标对象的特性来选择代理方式,我们来看这一段的源码
arduino
// DefaultAopProxyFactory.java
public AopProxy createAopProxy(AdvisedSupport config) {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config); // JDK 代理
}
return new ObjenesisCglibAopProxy(config); // CGLIB 代理
} else {
return new JdkDynamicAopProxy(config);
}
}
我们这里可以看到,具体的创建逻辑,是JDK代理还是CGLIB代理我们是看目标类是否实现了接口,并且没有强制要求CGLIB,则这时候我们采用JDK代理。否则是使用CGLIB代理,你也可以强行使用CGLIB代理。 @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB
这一块的内容还有些判断,就是要扫描类是否为基础设施类,这些是不能被代理的,然后判断当前类是否为用户排除的以及是否为 @Aspect 本身。
3. AOP与Spring IoC容器的整合
Spring AOP 与 Spring IoC 容器的整合,让 AOP 的增强能力无缝地融入到 Bean 的生命周期中,从而实现了所谓的 "面向切面编程天然具备依赖注入能力" 。那具体的整合点在哪呢?
3.1 Bean后置处理器
Spring AOP 的核心机制,是通过 Bean 的后置处理器(BeanPostProcessor) 来实现与 IoC 容器的整合。在容器初始化过程中,Spring 会注册一系列 BeanPostProcessor,其中最关键的是:
AnnotationAwareAspectJAutoProxyCreator
它是Spring AOP 的"代理生成器",也是一个特殊的 BeanPostProcessor,用于:在 IoC 容器实例化完 Bean 后,判断是否需要创建代理,并将增强逻辑织入 Bean 中。
3.2 生命周期中的整合流程
我们知道Bean的生命周期简单来看大概就是下面这个过程:
实例化->依赖注入->初始化(回调)->Bean Ready 所以AOP发生在哪呢?
实际上AOP代理增强就是在初始化之后,正式交付之前,这一步就是通过上面的AnnotationAwareAspectJAutoProxyCreator 来判断是否生成代理。这个类的来龙去脉我们要说一下,实际上这个类不是你写个@Aspect就冒出来的,而是在容器初始化时注册出来的。在你加上 @EnableAspectJAutoProxy 后,Spring 会通过 Import 机制注册一个 AspectJAutoProxyRegistrar ,最终把 AnnotationAwareAspectJAutoProxyCreator 加入 BeanFactory。这样它就参与到所有 Bean 的后置处理中了。
3.3 代理增强如何"织入Bean中"呢?
这里是整合的关键:
typescript
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 判断是否需要代理
Object wrappedBean = wrapIfNecessary(bean, beanName);
return wrappedBean != null ? wrappedBean : bean;
}
通过切点匹配Advisor判断是否要增强,需要增强->创建代理->返回代理对象,否则就原样返回。所以最终放回IoC容器的,其实是被代理的Bean,也就是AOP增强过的。所以我们可以看出,AOP和IoC是共用一个容器:
- AOP织入发生在容器中,受IoC生命周期管理
- 被代理的Bean其他不受影响,依旧可以是单例、可以注入、支持生命周期回调
这让Spring AOP完全嵌入到IoC到统一管理流程中。
4. 总结
到这里我们基本上可以总结出AOP的核心思想是什么了,如果用一句话概括,那我们可以这样描述:在不修改原始业务逻辑的前提下,将横切关注点以统一的方式织入目标对象的运行流程中 。
另外多说一嘴,在具体的方法调用上,被抽象成拦截器链。通过 MethodInvocation
的 proceed()
方法递归触发链式调用,实现通知(Advice)的顺序执行。这里责任链模式我后面会单独拿来说。这篇文章就说到这里,下篇文章我们就可以探讨JDK 与 CGLIB 动态代理的抉择与实现。
