深入理解Spring容器:从基础到原理(四十)
一、引言
在我们对Spring容器中AOP实现的深入探索中,已经详细剖析了AnnotationAwareAspectJAutoProxyCreator
在创建AOP代理过程中的基本流程,包括其在postProcessAfterInitialization
方法中的代理创建逻辑以及获取增强器的初步步骤。此刻,我们将聚焦于AnnotationAwareAspectJAutoProxyCreator
在获取增强器过程中的核心逻辑——buildAspectJAdvisors
方法及其内部的详细操作。理解这一过程对于全面掌握Spring AOP如何从Spring容器中提取切面定义、生成增强器并最终实现横切关注点的模块化管理至关重要。通过深入解析buildAspectJAdvisors
方法,我们将能够更好地运用Spring AOP来实现高效、灵活且易于维护的代码结构,提升应用程序的开发效率和质量。
二、获取增强器:buildAspectJAdvisors
方法的深入解析
(一)方法的整体功能概述
-
buildAspectJAdvisors
方法的主要功能是扫描Spring容器中的所有bean,找出那些被@Aspect
注解标记的类,并从这些类中提取增强器(Advisor
)。这些增强器将用于后续创建AOP代理时,将切面逻辑织入到目标对象的方法调用中。该方法在整个AOP实现过程中起着关键的桥梁作用,它将配置文件或注解中定义的切面信息转换为可用于代理创建的增强器集合,为实现AOP的核心功能奠定了基础。 -
例如,在一个包含多个业务模块和切面定义的Spring应用中,
buildAspectJAdvisors
方法会遍历容器中的所有bean,识别出如日志切面、事务切面等被@Aspect
注解标记的类,提取其中的增强逻辑(如日志记录方法、事务管理逻辑等)对应的增强器,以便在合适的时机将这些增强器应用到目标业务对象上,实现对业务逻辑的非侵入式增强。(二)获取所有beanName并筛选AspectJ注解类
1. 获取所有beanName
-
方法首先通过
BeanFactoryUtils.beanNamesForTypeIncludingAncestors
方法获取Spring容器中所有注册的bean的名称(beanNames
)。这一步骤确保了对容器中所有bean的全面扫描,不放过任何一个可能包含切面定义的bean。例如,在一个大型企业级应用中,可能存在众多的bean,beanNamesForTypeIncludingAncestors
方法会遍历整个bean工厂及其祖先工厂(如果存在),收集所有bean的名称,为后续的筛选和处理提供了完整的数据源。 -
假设我们有一个应用包含了用户管理模块、订单管理模块等多个模块,每个模块都有自己的bean定义。
beanNamesForTypeIncludingAncestors
方法会返回所有这些模块中定义的bean的名称,如"userService"
、"orderService"
、"loggingAspect"
、"transactionAspect"
等,其中"loggingAspect"
和"transactionAspect"
可能就是被@Aspect
注解标记的切面类的bean名称。2. 筛选AspectJ注解类
-
遍历获取到的所有
beanNames
,对于每个bean名称,首先调用isEligibleBean
方法判断该bean是否合法。虽然在当前代码中isEligibleBean
方法的默认实现是返回true
,但这为子类提供了一个扩展点,子类可以根据自己的需求定义更严格的合法性判断规则,例如根据bean的类型、作用域或其他自定义条件来决定是否处理该bean。 -
如果bean合法,通过
beanFactory.getType
方法获取bean的类型(beanType
)。如果获取到的类型为null
,则跳过该bean,继续处理下一个。对于获取到类型的bean,使用advisorFactory.isAspect
方法判断该bean类型是否被标记为@Aspect
注解。如果是,则将该bean名称添加到aspectNames
列表中,表明该bean是一个AspectJ切面类,需要进一步处理。例如,在遍历过程中,当遇到"loggingAspect"
这个bean名称时,获取其类型并判断是否为AspectJ切面类,如果是,则将其标记为需要进一步处理的AspectJ注解类。(三)提取增强器并处理不同实例化模型
1. 单例AspectJ类的增强器提取与缓存
-
对于被标记为AspectJ注解的类,如果其切面实例化模型为单例(通过
AspectMetadata
获取PerClauseKind
为SINGLETON
判断),会创建一个BeanFactoryAspectInstanceFactory
实例(factory
),用于在后续获取增强器时创建AspectJ切面实例。然后,调用advisorFactory.getAdvisors
方法获取该切面类中的增强器列表(classAdvisors
)。 -
如果该bean在Spring容器中是单例的(通过
beanFactory.isSingleton
判断),会将获取到的增强器列表缓存到advisorsCache
中,以提高性能,避免重复解析。例如,对于一个全局的日志切面类,它可能在整个应用中只需要一个实例,并且其增强器在整个应用生命周期中不会发生变化,将其增强器缓存起来可以在后续处理中快速获取,提高AOP的执行效率。2. 非单例AspectJ类的处理
-
如果AspectJ类的切面实例化模型不是单例(如
Per target
或Per this
),并且该bean在Spring容器中是单例的,会抛出IllegalArgumentException
异常,因为这种配置不符合预期。这是因为非单例的切面实例化模型通常需要为每个目标对象或每个特定的上下文创建独立的切面实例,而单例的bean在容器中只有一个实例,无法满足这种需求。 -
如果AspectJ类的切面实例化模型不是单例且bean在容器中不是单例(例如,在某些情况下可能使用原型bean作为切面实例),会创建一个
PrototypeAspectInstanceFactory
实例(factory
),并将其缓存到aspectFactoryCache
中。然后,同样调用advisorFactory.getAdvisors
方法获取增强器列表,并将其添加到总的增强器列表(advisors
)中。这确保了对于不同实例化模型的AspectJ类,都能够正确地提取增强器并进行相应的处理,以满足各种复杂的应用场景需求。(四)增强器缓存与最终返回
-
在处理完所有的bean后,
buildAspectJAdvisors
方法会根据aspectNames
列表构建最终的增强器列表(advisors
)。如果aspectNames
为空,表示没有找到任何AspectJ注解类,直接返回一个空列表(Collections.EMPTY_LIST
)。 -
如果
aspectNames
不为空,会遍历aspectNames
列表。对于每个AspectJ注解类的名称,首先尝试从advisorsCache
中获取缓存的增强器列表。如果缓存中存在,则将其添加到最终的增强器列表中;如果缓存中不存在,从aspectFactoryCache
中获取对应的MetadataAwareAspectInstanceFactory
实例,然后调用advisorFactory.getAdvisors
方法获取增强器列表,并添加到最终列表中。这样,通过缓存机制,避免了对AspectJ注解类的重复解析和增强器提取,提高了性能,尤其是在处理大量bean和复杂切面定义的情况下,缓存的作用更加显著。 -
最终,
buildAspectJAdvisors
方法返回包含所有提取到的增强器的列表,这些增强器将在后续创建AOP代理时被使用,用于为目标对象添加横切关注点的增强逻辑。(五)代码示例展示增强器获取过程
-
假设我们有一个简单的Spring应用,包含一个
CalculatorService
接口和其实现类CalculatorServiceImpl
,以及两个切面类LoggingAspect
用于日志记录和ValidationAspect
用于参数验证。我们希望通过AOP在CalculatorServiceImpl
的方法执行前后分别进行日志记录和参数验证。 -
首先,定义
CalculatorService
接口:public interface CalculatorService { int add(int num1, int num2); int subtract(int num1, int num2); }
-
然后,实现
CalculatorServiceImpl
类:import org.springframework.stereotype.Service; @Service public class CalculatorServiceImpl implements CalculatorService { @Override public int add(int num1, int num2) { return num1 + num2; } @Override public int subtract(int num1, int num2) { return num1 - num2; } }
-
接着,定义
LoggingAspect
切面类:import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.CalculatorServiceImpl.*(..))") public void logBeforeOperation() { System.out.println("Before calculator operation"); } }
-
再定义
ValidationAspect
切面类:import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class ValidationAspect { @Before("execution(* com.example.CalculatorServiceImpl.add(..))") public void validateAddParameters() { System.out.println("Validating add operation parameters"); } }
-
在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="calculatorService" class="com.example.CalculatorServiceImpl" /> <bean id="loggingAspect" class="com.example.LoggingAspect" /> <bean id="validationAspect" class="com.example.ValidationAspect" /> <aop:aspectj-autoproxy /> </beans>
-
当Spring容器启动并执行到
buildAspectJAdvisors
方法时:-
首先,通过
beanNamesForTypeIncludingAncestors
方法获取所有bean的名称,假设获取到"calculatorService"
、"loggingAspect"
、"validationAspect"
等名称。 -
然后,遍历这些bean名称。对于
"loggingAspect"
和"validationAspect"
,因为它们被标记为@Aspect
注解,会被识别为AspectJ注解类。 -
对于
"loggingAspect"
,假设其切面实例化模型为单例(默认情况),创建BeanFactoryAspectInstanceFactory
实例,并调用advisorFactory.getAdvisors
方法获取增强器列表,假设获取到一个Before
通知增强器(对应logBeforeOperation
方法),将其缓存到advisorsCache
中(因为loggingAspect
是单例bean)。 -
对于
"validationAspect"
,同样假设其切面实例化模型为单例,进行类似的处理,获取到一个Before
通知增强器(对应validateAddParameters
方法)并缓存。 -
最后,当需要为
CalculatorServiceImpl
创建AOP代理时,会从缓存中获取LoggingAspect
和ValidationAspect
的增强器列表,并将这些增强器应用到代理对象的创建中,使得在CalculatorServiceImpl
的add
方法执行前,会先执行validateAddParameters
方法进行参数验证,然后执行logBeforeOperation
方法进行日志记录,最后再执行add
方法的实际业务逻辑。这展示了buildAspectJAdvisors
方法如何从容器中提取增强器,并为后续的AOP代理创建提供必要的增强逻辑。三、总结与展望
通过对
AnnotationAwareAspectJAutoProxyCreator
的buildAspectJAdvisors
方法的深入剖析,我们全面了解了Spring AOP如何从Spring容器中扫描并提取AspectJ注解类的增强器。从获取所有beanName到筛选AspectJ注解类,再到提取增强器并处理不同的实例化模型,以及最终的缓存和返回增强器列表,每个环节都展示了Spring AOP在增强器获取过程中的精细设计和高效处理机制。在后续的学习中,我们将继续探索Spring AOP的更多高级特性,如增强器的优先级控制、切面的执行顺序、AOP与其他Spring组件的集成细节以及在实际应用中的性能优化等。这些内容将进一步拓展我们对Spring AOP的理解和应用能力,使我们能够构建更加健壮、高效和灵活的Java应用程序。希望本文能够帮助码龄1 - 5年的程序员更好地理解Spring AOP的这一重要实现过程,为开发高质量的应用提供有力的技术支持。让我们共同期待下一次的深入探索之旅,继续挖掘Spring框架的无尽潜力。
-