本文基于Spring AOP 实践 和 Spring AOP XML配置方式原理详解, 来继续讲解 基于注解方式实现AOP 自动代理的原理。
在Spring AOP 实践一文中,我们探讨了通过在XML配置文件中使用ProxyFactoryBean
来配置代理对象的方法,并在需要时手动获取代理对象以实现AOP功能。
然而,在大型生产环境中,这种配置方式显得繁琐且不切实际。为了解决这一问题,Spring提供了一种基于AbstractAutoProxyCreator
的自动代理机制,使得我们无需为每个Bean手动配置ProxyFactoryBean
。
AbstractAutoProxyCreator
通过自动检测Bean的类型和相应的切面(Aspect)来创建代理对象,从而简化了配置过程。
注解方式实现的AOP 自动代理机制使用的是AbstractAutoProxyCreator
的子类AnnotationAwareAspectJAutoProxyCreator
如果你已经了解了
那么现在来理解注解方式的自动代理机制其实已经比较容易了。
接下来,我们将以一个Spring Boot项目为示例,探讨AnnotationAwareAspectJAutoProxyCreator
实现自动代理的原理。
0. 示例代码
1 |
|
1. AnnotationAwareAspectJAutoProxyCreator 继承、实现关系解析
AnnotationAwareAspectJAutoProxyCreator
是Spring AOP实现的核心类之一用于在Spring容器中扫描和处理带有@Aspect
注解的bean。
AnnotationAwareAspectJAutoProxyCreator
继承关系较为复杂,它是多个类和接口的组合体,主要继承关系如下:
1.1 BeanPostProcessor
在 Spring IOC 容器启动过程拓展点一文中,已经详细讲解过BeanPostProcessor
的作用,此处不再赘述,直接来看它的3个子类
1.1.1 InstantiationAwareBeanPostProcessor
图中置灰的2个方法,是继承自BeanPostProcessor
的2个方法
InstantiationAwareBeanPostProcessor
自有的3个方法中有2个方法名称和BeanPostProcessor
中方式名称相同,但是入参和返回值略有不同
- postProcessBeforeInstantiation
1
2
3
4
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
在bean 的实例化之前,会调用这个方法,其逻辑存在于下面代码中resolveBeforeInstantiation
方法中。 这个方法可以返回一个代理对象,来替代 Bean 的默认实例化过程。但通常情况下,代理对象是在 Bean 实例化之后创建的,所以这个方法一般返回 null
。
1 |
|
- postProcessAfterInstantiation
在 bean create之后,populate之前调用。返回true
表示允许属性注入,返回false
则跳过属性注入。
1 | default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { |
- postProcessProperties
在populate 属性注入过程中调用,可以对属性值进行检查或修改。1
2
3
4
5
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
1.1.2 SmartInstantiationAwareBeanPostProcessor
SmartInstantiationAwareBeanPostProcessor
是 Spring 框架中的一个接口,它扩展了 InstantiationAwareBeanPostProcessor
接口,提供了更细粒度的控制和额外的钩子方法来介入 Spring Bean 的实例化过程。这个接口主要用于在 Bean create之前、create之后、populate之前、populate之后等多个阶段执行自定义逻辑,从而实现更加复杂和灵活的 Bean 初始化控制。
predictBeanType
在实际创建 Bean 实例之前预测其类型。这对于提前检测某些类型相关的元数据很有用。1
2
3default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
return null;
}determineCandidateConstructors
确定要用于创建 Bean 实例的候选构造函数。允许自定义选择用于实例化 Bean 的构造函数。1
2
3
4
5default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {
return null;
}getEarlyBeanReference
允许在 Bean 实例化之前提前暴露 Bean 的引用。通常用于解决循环依赖问题1
2
3default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
1.2 AbstractAutoProxyCreator
AbstractAutoProxyCreator
是一个抽象类,它继承自 ProxyProcessorSupport
并实现了 SmartInstantiationAwareBeanPostProcessor
和 BeanFactoryAware
接口。这些接口和类提供了创建代理对象和与 Spring 容器集成的基础功能。
它的主要作用是自动为 Spring 容器中的 bean 创建代理对象,以便在这些 bean 的方法调用时插入横切关注点的逻辑。具体来说,AbstractAutoProxyCreator
的功能包括:
- 扫描和筛选 bean:在 Spring 容器初始化时,扫描所有 bean,根据配置的切点筛选出需要增强的 bean。
- 创建代理对象:为符合条件的 bean 创建代理对象,代理对象可以是 JDK 动态代理或 CGLIB 代理。
- 注册代理对象:将创建的代理对象注册到 Spring 容器中,替换原始的 bean。
- 处理生命周期回调:实现
BeanPostProcessor
接口中的postProcessBeforeInitialization
和postProcessAfterInitialization
方法,在 bean 初始化前后执行代理逻辑。
1 | public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport |
下面来分析一下AbstractAutoProxyCreator
的具体子类
1.2.1 AbstractAdvisorAutoProxyCreator
- 作用:这个类是
AbstractAutoProxyCreator
的直接子类,它的主要作用是自动为 Spring Bean 创建代理对象,并将配置的切面(advice)应用到这些代理对象上。 - 工作原理:它扫描 Spring 容器中的所有
Advisor
(一个Advisor
包含一个Advice
和一个Pointcut
),并根据Pointcut
的匹配规则,自动为匹配的 Bean 创建代理对象,并应用对应的Advice
。
1.2.2 AspectJAwareAdvisorAutoProxyCreator
作用:这个类是 AbstractAdvisorAutoProxyCreator
的子类,主要用于处理基于 AspectJ 方式的 AOP 配置。
工作原理:它和 AnnotationAwareAspectJAutoProxyCreator
类似,但主要用于处理通过 XML 配置文件或其他非注解方式配置的 AspectJ 切面。
1.2.3 AnnotationAwareAspectJAutoProxyCreator
- 作用:这个类是
AspectJAwareAdvisorAutoProxyCreator
的子类,它在AbstractAdvisorAutoProxyCreator
的基础上增加了对 AspectJ 注解的支持。 - 工作原理:它不仅扫描容器中的
Advisor
,还扫描使用了 AspectJ 注解(如@Aspect
、@Before
、@After
等)的 Bean,并将这些注解配置的切面应用到匹配的 Bean 上。
2. 自动代理工作流程
2.1 AnnotationAwareAspectJAutoProxyCreator的实例化
2.1.1 registerBeanPostProcessors
我们已经知道AnnotationAwareAspectJAutoProxyCreato
r其本质一个BeanPostProcessor
在 [Spring IOC 容器启动过程拓展点](obsidian://open?vault=Documents&file=second-brain%2F1-tech%2FSpring%20%E5%AE%B6%E6%97%8F%2Fnew%2F3-%20Spring%20%E6%8B%93%E5%B1%95%E7%82%B9) 一文中,已经详细介绍过
一个
BeanPostProcessor的实例化或者注册过程,是在
registerBeanPostProcessors`流程中
1 | AbstractApplicationContext.java |
展开registerBeanPostProcessors
看一下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21PostProcessorRegistrationDelegate.java
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
}
AbstractBeanFactory.java
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
// Remove from old position, if any
this.beanPostProcessors.remove(beanPostProcessor);
// Track whether it is instantiation/destruction aware
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true;
}
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true;
}
// Add to end of list
this.beanPostProcessors.add(beanPostProcessor);
}
2.1.2 hasInstantiationAwareBeanPostProcessors
根据上面的继承依赖结构,可以看出AnnotationAwareAspectJAutoProxyCreator
实现了 InstantiationAwareBeanPostProcessor
,所以hasInstantiationAwareBeanPostProcessors
会被设置为true
- 状态标记: 当Spring容器初始化时,会扫描并注册所有的
BeanPostProcessor
,并在适当的时候设置这个变量的值。如果容器中注册了至少一个InstantiationAwareBeanPostProcessor
,这个变量会被设置为true
。用于决定是否执行某些特定的处理逻辑。例如,在实例化bean之前,Spring容器会检查这个变量以决定是否需要调用InstantiationAwareBeanPostProcessor
的postProcessBeforeInstantiation
方法。 - 性能提升:如果没有注册任何
InstantiationAwareBeanPostProcessor
,则可以跳过一些不必要的检查和调用,从而提高性能。
2.2 代理对象的生成时机- init 阶段
业务bean 代理对象的生成时机,也可以理解成是AnnotationAwareAspectJAutoProxyCreator
的介入 bean 的生命周期,为业务bean生成代理对象的时机。
在 Spring bean 实例化一文中,已经介绍过,BeanPostProcessor
是在bean 实例化的init 阶段,通过 postProcessAfterInitialization
方法来介入普通bean 的生命周期,为其生成代理对象的
1 | public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory |
当遇到AnnotationAwareAspectJAutoProxyCreator
时, 会进行其postProcessAfterInitialization
方法,开始执行代理对象生成的逻辑,下图debug 图片中,可以看到相关信息, 以及实际进入到的是AnnotationAwareAspectJAutoProxyCreator
的父类,AbstractAutoProxyCreator
2.3 wrapIfNecessary
在SpringBoot 项目启动过程中,所有的业务bean 实例化过程中都在在initializeBean 阶段进入AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization
方法,只要这个拓展点存在。也就是说,所有的bean都会进行是否代理的判断,只是有的不需要直接返回原bean即可,有的需要就行进行代理对象的生成并返回
1 | public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport |
wrapIfNecessary
方法的重点逻辑看2步
- getAdvicesAndAdvisorsForBean 为当前类寻找可用的增强
- createProxy 如果找到了可用的增强,则创建代理对象
2.4 getAdvicesAndAdvisorsForBean
为当前类寻找可用的增强
1 | public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator { |
最终得到当前类可用的bean, 代理逻辑挺深的
- 找到当前Spring 容器中所有的Advisor
- 找到可以用在当前类的Advisor :对找到的所有的Advisor循环判断是否可以用在当前类上
我们重点看第二步,findAdvisorsThatCanApply 这个方法
2.4.1 findAdvisorsThatCanApply->canApply
在一组候选的Advisor
中,找出可以应用于指定类的Advisor
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82public abstract class AopUtils {
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
// 先处理 IntroductionAdvisor
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
// 再处理其他类型的Advisor
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
// 对于IntroductionAdvisor,canApply(candidate, clazz)最终也是使用这个方法
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
// 这里是真正的过滤逻辑, 先匹配类,再匹配方法
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
}
整体逻辑
- 先把Advisor 分成两类,
IntroductionAdvisor
和其他Advisor, 先检查和处理IntroductionAdvisor
的主要原因是它们对目标类结构的影响较大,通过引入新的接口和功能,改变了目标类的形态。因此,在处理其他常规Advisor
之前,确保IntroductionAdvisor
已经被正确应用 - 对Advidor 依次进行类匹配、方法匹配, 看是否符合切点表达式,在以下方法中具体实现
Advisor
是一个复合概念,包括配套的Pointcut
和Advice
。
Pointcut
中包含了对类的过滤条件ClassFilter
、方法的过滤条件MethodMatcher
Advice
是符合Pointcut
切点条件的方法上需要执行的增强逻辑。
1 | canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) |
观察这个方法的过滤逻辑, 其实和 Spring AOP XML配置方式原理详解中getInterceptorsAndDynamicInterceptionAdvice
方法的逻辑是一样的,都是通过ClassFilter
匹配类,以及MethodMatcher
匹配方法实现, 只不过在手动配置方式中,我们需要手动显示写明类去自定义实现 Pointcut、MethodMatcher ,但是在自动代理的注解方式中, Spring 会自动按照我们给出的切点表达式,自动实现这一功能。
2.5 createProxy
1 | public class ProxyFactory extends ProxyCreatorSupport { |
逻辑从这里开始就和 Spring AOP 注解方式原理详解中开始完全重合了,就不再赘述
- createAopProxy: 确定动态代理的类型, JDK 动态代理 or CGlib, 判断的逻辑也非常简单,可以简单认为就是有没有实现接口
- 实现了接口,用JDK 动态代理
- 没有实现接口,用CGlib 动态代理
- getProxy:根据确定的动态代理,创建target 的代理对象
最终给userService 生成了一个Cglib 动态代理对象
2.6 执行目标方法
不论是JDK 动态代理对象,还是CGLIB 动态代理对象, 执行目标方法时和
和 Spring AOP XML配置方式原理详解 中的逻辑完全一致,底层是公用事业一套代码逻辑的,这里就不再赘述。
3. 代理对象的获取方式对比
手动配置方式使用ProxyFactoryBean
, 不介入到bean得生命周期,而是在实例化完成后通过getObjectForBeanInstance
完成代理对象的生成
注解方式的自动代理机制,介入bean 的生命周期,在init 阶段,通过BeanPostProcessor.postProcessAfterInitialization
获取代理对象。
但是不论哪种方式, 底层判断使用哪种代理,增强织入的逻辑、以及方法的执行均是使用同一套代码完全相同的逻辑