深入理解Spring容器:从基础到原理(三十九)
一、引言
在我们对Spring容器功能扩展以及AOP实现的持续探索中,已经深入剖析了动态AOP自定义标签的解析过程,了解了Spring如何通过配置启用AOP并注册关键组件。此刻,我们将聚焦于AOP实现的核心环节——创建AOP代理。AnnotationAwareAspectJAutoProxyCreator
作为实现AOP操作的关键类,在Spring容器加载bean时发挥着重要作用。通过深入研究其在创建AOP代理过程中的行为,包括获取增强方法或增强器以及创建代理的具体步骤,我们将能够全面掌握Spring AOP如何将切面逻辑织入目标对象,实现横切关注点的模块化管理。这对于构建高效、灵活且易于维护的Java应用程序具有重要意义,能够帮助我们更好地利用AOP特性解决实际开发中的问题,如日志记录、事务管理、权限控制等。
二、创建AOP代理:核心流程解析
(一)AnnotationAwareAspectJAutoProxyCreator
的角色与入口方法
-
AnnotationAwareAspectJAutoProxyCreator
在Spring AOP中扮演着至关重要的角色,它实现了BeanPostProcessor
接口。这意味着在Spring加载bean时,会在实例化后调用其postProcessAfterInitialization
方法。这个方法就是我们分析AOP逻辑的入口点,它负责判断是否需要为给定的bean创建代理对象,并在必要时进行代理创建和增强逻辑的织入。 -
例如,在一个包含多个业务bean和切面定义的Spring应用中,当每个bean实例化完成后,
AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization
方法都会被调用,以决定是否对该bean进行AOP增强。假设我们有一个UserService
bean,当它被实例化后,这个方法会根据配置的切面和切点判断是否需要为UserService
创建代理,从而实现诸如日志记录、事务管理等功能的织入。(二)
postProcessAfterInitialization
方法中的代理创建逻辑1. 缓存键的构建与早期代理引用检查
-
在
postProcessAfterInitialization
方法中,首先会根据给定bean的类和名称构建一个缓存键(cacheKey
),格式为beanClassNarne beanNarne
。这个缓存键用于后续的缓存操作,例如判断bean是否已经被处理过或者是否适合被代理。然后,检查earlyProxyReferences
集合中是否包含该缓存键。如果包含,表示该bean已经在早期被引用过(可能在循环依赖处理等情况下),此时直接返回bean本身,不再进行代理创建操作。 -
例如,在一个复杂的对象依赖关系中,如果
UserService
在创建过程中已经被其他对象提前引用,并且已经处理过其代理相关逻辑,那么当再次在postProcessAfterInitialization
方法中遇到UserService
时,会直接返回原始的UserService
实例,避免重复创建代理。2. 判断是否需要创建代理
-
如果
earlyProxyReferences
集合中不包含缓存键,接下来会调用wrapIfNecessary
方法来判断是否需要为bean创建代理。在wrapIfNecessary
方法中,首先会检查targetSourcedBeans
集合中是否包含bean的名称。如果包含,表示该bean已经被处理过,直接返回bean本身。然后,检查nonAdvisedBeans
集合中是否包含缓存键。如果包含,表示该bean不需要增强,直接返回bean。 -
接着,判断给定的bean类是否代表一个基础设施类(通过
isinfrastructureClass
方法判断)或者是否应该跳过该bean的代理创建(通过shouldSkip
方法判断,例如根据bean的类型或名称等条件)。如果是基础设施类或者应该跳过,将缓存键添加到nonAdvisedBeans
集合中,并返回bean本身。例如,Spring内部的一些基础设施类可能不需要进行AOP增强,或者开发者可以根据自己的需求指定某些bean不进行代理。3. 获取增强方法或增强器
-
如果经过上述判断后确定需要为bean创建代理,就会调用
getAdvicesAndAdvisorsForBean
方法来获取适用于该bean的增强方法或增强器。这个方法首先会调用findEligibleAdvisors
方法来获取所有可能的增强器,然后筛选出适用于当前bean的增强器。在findEligibleAdvisors
方法中,又会先调用findCandidateAdvisors
方法获取所有的候选增强器,再调用findAdvisorsThatCanApply
方法从候选增强器中找出适用于bean的增强器。 -
例如,对于一个
UserService
bean,findCandidateAdvisors
方法会查找配置文件中定义的所有切面以及通过注解定义的切面,并获取它们对应的增强器。然后,findAdvisorsThatCanApply
方法会根据UserService
的类和方法信息,筛选出那些切点匹配UserService
方法的增强器。假设我们有一个LoggingAspect
切面,定义了一个切点来匹配UserService
的所有方法,那么在这个过程中,LoggingAspect
中的增强逻辑(如日志记录方法)对应的增强器就会被筛选出来作为适用于UserService
的增强器。4. 创建代理对象
-
如果获取到了增强器(即
getAdvicesAndAdvisorsForBean
方法返回的数组不为null
),表示需要为bean创建代理。此时,会调用createProxy
方法来创建代理对象。在createProxy
方法中,会根据bean的类、名称、增强器和目标源(SingletonTargetSource
,包含了原始的bean实例)等信息创建代理对象。创建代理对象的过程涉及到根据配置选择合适的代理方式(如JDK动态代理或CGLIB代理),并将增强器织入到代理对象的方法调用逻辑中。 -
例如,如果
UserService
需要进行AOP增强,并且获取到了LoggingAspect
的增强器,createProxy
方法会创建一个代理对象,该代理对象在调用UserService
的方法时,会先执行LoggingAspect
中的日志记录逻辑,然后再调用原始的UserService
方法,实现了日志记录功能的织入。代理对象创建完成后,会将其缓存起来(通过proxyTypes
集合),并返回代理对象。如果没有获取到增强器,会将缓存键添加到nonAdvisedBeans
集合中,并返回原始的bean。(三)获取增强器:
findCandidateAdvisors
方法解析1. 继承父类的配置文件增强获取
-
AnnotationAwareAspectJAutoProxyCreator
间接继承了AbstractAdvisorAutoProxyCreator
,在findCandidateAdvisors
方法中,首先会调用父类的findCandidateAdvisors
方法来获取配置文件中定义的增强器。这意味着Spring在使用注解方式配置AOP时,并没有丢弃对XML配置的支持,仍然可以从配置文件中读取AOP相关的声明,并将其转换为增强器。 -
例如,在一个既有XML配置又有注解配置AOP的项目中,通过这种方式,配置文件中定义的切面和增强逻辑(如
<aop:config>
标签下定义的切面)会被解析并获取相应的增强器,与注解方式定义的增强器共同参与后续的增强器筛选和代理创建过程。2. 添加注解增强的获取
-
在获取配置文件中的增强器后,
AnnotationAwareAspectJAutoProxyCreator
会添加获取基于注解的增强功能。具体通过调用this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
方法来实现。这个方法会扫描Spring容器中的bean,查找那些被@Aspect
注解标记的类,并解析其中的切面定义,将其转换为增强器。 -
例如,假设有一个
LoggingAspect
类,使用@Aspect
注解进行标记,并定义了一些切点和增强方法(如@Before
、@After
等通知方法)。在buildAspectJAdvisors
方法执行时,会发现LoggingAspect
类,解析其切面定义,创建相应的增强器,并将这些增强器添加到候选增强器列表中。这样,无论是通过配置文件还是注解定义的增强器,都能够被统一管理和使用,为后续的代理创建提供了丰富的增强逻辑来源。(四)代码示例展示创建AOP代理过程
-
假设我们有一个简单的Spring应用,包含一个
ProductService
接口和其实现类ProductServiceImpl
,以及一个切面类PerformanceAspect
用于性能监控。我们希望通过AOP在ProductServiceImpl
的方法执行前后进行性能监控,记录方法的执行时间。 -
首先,定义
ProductService
接口:public interface ProductService { void addProduct(String productName); void updateProduct(String productName); }
-
然后,实现
ProductServiceImpl
类:import org.springframework.stereotype.Service; @Service public class ProductServiceImpl implements ProductService { @Override public void addProduct(String productName) { System.out.println("Adding product: " + productName); } @Override public void updateProduct(String productName) { System.out.println("Updating product: " + productName); } }
-
接着,定义
PerformanceAspect
切面类:import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class PerformanceAspect { @Around("execution(* com.example.ProductServiceImpl.*(..))") public Object measurePerformance(ProceedingJoinPoint pjp) throws Throwable { long startTime = System.currentTimeMillis(); Object result = pjp.proceed(); long endTime = System.currentTimeMillis(); System.out.println(pjp.getSignature() + " took " + (endTime - startTime) + " ms"); return result; } }
-
在Spring配置文件中,配置AOP相关信息(这里假设使用XML配置与注解配置混合的方式,仅展示关键部分):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframeworkframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframeworkframework.org/schema/aop" xsi:schemaLocation="http://www.springframeworkframework.org/schema/beans http://www.springframeworkframework.org/schema/beans/Spring-beans.xsd http://www.springframeworkframework.org/schema/aop http://www.springframeworkframework.org/schema/aop/Spring-aop.xsd"> <bean id="productService" class="com.example.ProductServiceImpl" /> <bean id="performanceAspect" class="com.example.PerformanceAspect" /> <aop:aspectj-autoproxy /> </beans>
-
当Spring容器启动并创建
ProductServiceImpl
的bean时:-
首先,
AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization
方法会被调用。 -
构建缓存键,假设为
"com.example.ProductServiceImpl productService"
。检查earlyProxyReferences
集合,由于是首次处理,不包含该缓存键。 -
然后,在
wrapIfNecessary
方法中,检查targetSourcedBeans
和nonAdvisedBeans
集合,均不包含相关信息。接着判断ProductServiceImpl
不是基础设施类且不应跳过代理创建。 -
调用
getAdvicesAndAdvisorsForBean
方法获取增强器。先调用findEligibleAdvisors
方法,其中findCandidateAdvisors
方法会获取配置文件中可能存在的增强器(这里假设没有)以及通过buildAspectJAdvisors
方法获取PerformanceAspect
切面定义的增强器(因为切点匹配ProductServiceImpl
的方法)。然后findAdvisorsThatCanApply
方法筛选出适用于ProductServiceImpl
的增强器,即PerformanceAspect
的增强器。 -
由于获取到了增强器,调用
createProxy
方法创建代理对象。根据ProductServiceImpl
的类信息和PerformanceAspect
的增强器,选择合适的代理方式(假设使用JDK动态代理,因为ProductServiceImpl
实现了接口)创建代理对象。代理对象在调用addProduct
或updateProduct
方法时,会先执行PerformanceAspect
中的measurePerformance
方法来记录执行时间,然后再调用原始的ProductServiceImpl
方法。 -
最后,返回创建的代理对象,该代理对象将代替原始的
ProductServiceImpl
对象被应用程序使用,从而实现了性能监控功能的织入,在不修改ProductServiceImpl
源代码的情况下,增强了其功能。三、总结与展望
通过对
AnnotationAwareAspectJAutoProxyCreator
在创建AOP代理过程中的详细剖析,我们全面了解了Spring AOP如何在bean实例化后,根据配置的切面和切点,判断是否需要创建代理对象,并完成增强逻辑的织入。从postProcessAfterInitialization
方法的入口逻辑,到获取增强器的复杂过程,再到最终创建代理对象的实现,每个环节都展示了Spring AOP的强大功能和精细设计。在后续的学习中,我们将继续探索Spring AOP的更多高级特性,如切面的优先级控制、切点表达式的高级用法、AOP与其他Spring组件的集成细节等。这些内容将进一步拓展我们对Spring AOP的理解和应用能力,使我们能够构建更加健壮、高效和灵活的Java应用程序。希望本文能够帮助码龄1 - 5年的程序员更好地理解Spring AOP的这一核心实现过程,为开发高质量的应用提供有力的技术支持。让我们共同期待下一次的深入探索之旅,继续挖掘Spring框架的无尽潜力。
-