前言
以前写过一篇文章, 专门讲了spring的动态代理实现原理 从代理模式再出发!Proxy.newProxyInstance的秘密, 这一次我们探究下动态代理模式比较重量级的应用 – Spring AOP 的源码实现. 本文重在讲主流程, 为了让流程更清楚, 本篇会补上很多Spring IOC 的知识.
ConfigurationClassPostProcessor
ConfigurationClassPostProcessor的由来
都知道要用 Spring AOP, 需要先引入 spring-boot-starter-aop
的组件, 我们用的 Spring boot 版本是 2.4.4
, 接下来就说明下这个组件是如何启用的.
首先我们介绍IOC组件中比较重要的类 – BeanDefinitionRegistryPostProcessor
, 先看一下类注释:
拓展了标准的 BeanFactoryPostProcessor SPI 机制, 允许在 BeanFactoryPostProcessor 介入操作前 注册bean definition 到容器中.
Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.
然后总结下看下文之前必须要知道的三个类:
BeanDefinitionRegistryPostProcessor
侧重于注册BeanDefinition对象的信息. 这是spring对外开放的一个拓展(SPI),方便第三方注册自己的组件到Spring IOC.BeanFactoryPostProcessor
侧重于修改BeanDefinition对象的信息.BeanDefinitionRegistry
是管理 BeanDefinition的大总管,当然也可以注册修改BeanDefinition对象的信息 ,是 beanfactory 必须实现的接口.
知道了 BeanDefinitionRegistryPostProcessor
的作用, 然后我们介绍本节的主角 – ConfigurationClassPostProcessor
。
ConfigurationClassPostProcessor
实现了 BeanDefinitionRegistryPostProcessor
, 它的功能在于处理@Configuration class, 试图通过@Configuration class 找到更多需要被IOC管理的类对象. 这个类是整个spring启动流程最先注册实例化的类.下面是它的注册过程:
//从 SpringApplication main 方法出发, 跟踪到createApplicationContext()方法, 这是在创建ApplicationContext.
- class SpringApplication
- run(String... args)
- createApplicationContext()
//然后初始化了一个 AnnotationConfigApplicationContext(),这个初始化方法很重要, 它往spring容器注册了ConfigurationClassPostProcessor,
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
//继续初始化 AnnotatedBeanDefinitionReader, 最后执行 registerAnnotationConfigProcessors(),这个方法直接把ConfigurationClassPostProcessor注册成bean definition放到beanDefinitionMap中. 顺便多一嘴, AnnotationConfigApplicationContext实现了BeanDefinitionRegistry接口.所以按照前文所说, 它是BeanDefinition 的大管家,就有能力管理所有的 BeanDefinition. `BeanDefinitionRegistryPostProcessor` 和 `BeanDefinitionRegistry` 这些概念别搞混了.
....
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
....
启动类不能忘
到目前为止, 已经把 ConfigurationClassPostProcessor
的由来说明白了, 现在继续讲ConfigurationClassPostProcessor
干的事情.之前说过, ConfigurationClassPostProcessor
主要是处理@Configuration class, 那理论上第一个应该被ConfigurationClassPostProcessor
处理的类是什么? 应该是 执行main函数的启动类
.
在启动类上, @SpringBootApplication内部其实有几个关键的注解, 我们现在先关注@Configuration,说明DemoApplication类也是一个配置类,需要被ConfigurationClassPostProcessor
处理. 那么 DemoApplication
什么时候注册成bean definition,然后放到beanDefinitionMap中呢?
//run()方法有两个参数,参照注释, 一个是启动类的class对象(primarySource), 一个是启动参数. 这个class对象就很关键.
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
//构造器里把primarySources赋值给局部变量.
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
....
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
....
}
//经过一系列调用链把primarySources注册成bean definition,这里省略很多,大家可以自行调试
- class SpringApplication
- run(String... args)
- prepareContext(...)
- load(...)
- loader.load()
...
- registerBean
invokeBeanFactoryPostProcessors
到此为止, IOC容器一共注册了7个BeanDefinition, 包括了2个对目前流程最重要的类
- org.springframework.context.annotation.internalConfigurationAnnotationProcessor(ConfigurationClassPostProcessor)
- com.example.demo.DemoApplication (启动类)
现在我们看看 ConfigurationClassPostProcessor
是如何起作用的. 对于spring 的IOC流程, 我们应该知道大名鼎鼎的refresh()方法, 它解析我们给的ioc配置(java-base config,xml etc),并生成对象. 在执行 invokeBeanFactoryPostProcessors 的时候, 就会遍历 之前预置的 BeanDefinitionRegistryPostProcessor 对象
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanDefinitionRegistry(registry);
postProcessBeanDefRegistry.end();
}
}
//正式进入ConfigurationClassPostProcessor 的 processConfigBeanDefinitions 方法
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
//这里获取到 到目前位置找到的7个BeanDefinition, 逐个循环找 Configuration classes
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//具体的查找逻辑,选取里面重要的一行判断,看到 metadata.getAnnotationAttributes(Configuration.class.getName()) 是不是就一目了然了.
// Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//找到 demoApplication 是Configuration classes
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 之前大家用的很多的 @Order, 会在这里进行重新排序
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
//ConfigurationClassParser 开始解析 demoApplication ,解析的目的是为了获得 demoApplication 导入的其它对象
do {
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
....
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
....
}
while (!candidates.isEmpty());
继续跟代码,doProcessConfigurationClass
方法比较关键,我们都知道 @ComponentScan 注解很关键,它几乎扫描全项目所有的需要 Spring 托管的对象,而 @ComponentScan 就在下述代码中被扫描处理,ComponentScan 的扫描细节就直接忽略。
- parse: ConfigurationClassParser
- doProcessConfigurationClass: ConfigurationClassParser
- doScan: ClassPathBeanDefinitionScanner
- ...
//doProcessConfigurationClass 代码逻辑中扫描 Configuration class 是否有 @ComponentScan,有的话就扫描注册所有的 @Component 对象
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//扫描 Configuration class 是否有 @Import,有的话就注册@Import要导入的类
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//同理处理 @ImportResource
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
AopAutoConfiguration
经过 @ComponentScan 扫描,类 TestAspectJ
和 TestService
被Spring找到并注册到 beanDefinitionMap( TestAspectJ
和 TestService
的内容这一篇并不重要,下一篇会展示出来), 但是以前用过 AOP 的都知道, 开启 Spring AOP 需要一个注解 @EnableAspectJAutoProxy,在启动类 DemoApplication
上并没有标注这个注解,因为 spring boot 有一个 spring-boot-autoconfigure 模块, autoconfigure 会自动帮我们启用一些组件, 就比如我们现在需要的 aop
模块
我们先看看 spring-boot-autoconfigure
模块里面的 AopAutoConfiguration
代码
//这个配置默认是开启的,因为从 AopAutoConfiguration的注解 @ConditionalOnProperty来看, `spring.aop.auto matchIfMissing` 为true, 说明`spring.aop.auto`不存在显性配置的时候, 条件就是成立的, 而且根据 `spring.aop.proxy-target-class` 的设置,默认会先启用CglibAutoProxyConfiguration,也就是使用cglib来实现aop的逻辑
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
}
那上面的 AopAutoConfiguration
是怎么被 Spring 启动的时候加载的呢? 我们还要接着上章 doProcessConfigurationClass
方法说起,之前它解析了启动类 DemoApplication
的 @ComponentScan 注解,下面还继续解析了 @Import 注解
class ConfigurationClassParser doProcessConfigurationClass
// Process any @Import annotations
// 这个方法处理了 DemoApplication 里面的 @Import(AutoConfigurationImportSelector.class) 注解,并把 AutoConfigurationImportSelector 对象放进了 deferredImportSelectors list中。
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 等 DemoApplication 被彻底 parse 完成,在方法返回时,会最后处理 deferredImportSelectors list .
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
......
}
......
}
//最后处理 deferredImportSelectors list 中的元素
this.deferredImportSelectorHandler.process();
}
//解析deferredImportSelectors list的流程逻辑
- this.deferredImportSelectorHandler.process();
- processGroupImports()
- grouping.getImports()
- this.group.process
//回调AutoConfigurationImportSelector的getAutoConfigurationEntry方法, 找到 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
- AutoConfigurationImportSelector getAutoConfigurationEntry
//因为 AopAutoConfiguration 也有注解 @Configuration, 所以按照 @Configuration class的逻辑去处理(执行跟启动类DemoApplication一样的逻辑)
- processConfigurationClass()
这里再稍微细究下AutoConfigurationImportSelector的getAutoConfigurationEntry方法怎么找的。
SpringFactoriesLoader
类专门负责轮询扫描所有的 META-INF/spring.factories
文件,当你传参 org.springframework.boot.autoconfigure.EnableAutoConfiguration
时,就会从 spring-boot-autoconfigure
包里的 META-INF/spring.factories
搜索 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration
的对象列表.
@Import 导入的类并没有直接被注册到 beanDefinitionMap,等 demoApplication
被 ConfigurationClassParser
解析完毕后,通过 this.reader.loadBeanDefinitions(configClasses) 方法注册到 beanDefinitionMap。
....
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//将parser解析结果注册到 beanDefinitionMap
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
....
我们总结下这一章, 首先 spring aop 的启用得益于 spring-boot-autoconfigure
模块. 而 spring-boot-autoconfigure
模块是通过启动类的@Import(AutoConfigurationImportSelector.class)
导入的. 所以说别看启动类就一个文件,它在项目启动过程中做了很重要的角色.
AnnotationAwareAspectJAutoProxyCreator
上面我们千辛万苦把 JdkDynamicAutoProxyConfiguration 注册到 beanDefinitionMap(默认应该是启用CglibAutoProxyConfiguration,但是我们配置了 spring.aop.proxy-target-class=false
), JdkDynamicAutoProxyConfiguration本身并不重要,重要的在于里面的 @Import(AspectJAutoProxyRegistrar.class),在 this.reader.loadBeanDefinitions(configClasses) 的过程中,会执行 registerBeanDefinitions 回调方法. 并且把 AnnotationAwareAspectJAutoProxyCreator 对象注册进来.
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 往beandefinition注册 `AnnotationAwareAspectJAutoProxyCreator`
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
}
}
AnnotationAwareAspectJAutoProxyCreator 是一个 InstantiationAwareBeanPostProcessor(BeanPostProcessor)对象,这个对象作用非常大, 我们看一下它的注释, 处理所有的@AspectJ修饰过的类,为什么要处理这个类? 因为所有的 AOP 逻辑(代理谁,代理的逻辑)都写在@AspectJ修饰过的类里, Spring需要一个专门的组件来收集处理我们在@AspectJ类定义的AOP信息, 而这就是AnnotationAwareAspectJAutoProxyCreator需要处理的事.
处理所有的@AspectJ修饰过的类
AspectJAwareAdvisorAutoProxyCreator subclass that processes all AspectJ annotation aspects in the current application context.
料已备好,等待下菜
这一篇我们并没有说 AOP 在 Spring 是怎么大显神威的. 而是先介绍了一系列我们接下来必不可少的组件. 这些组件对于理解 Spring 支持 AOP 特别重要. 我们再简单复习下:
ConfigurationClassPostProcessor
. Spring 启动首批注册的组件, 专门用来处理 @Configuration class, 正因为有它,才能引出其它待引入的组件AutoConfigurationImportSelector
. 写在启动类@Import里,ConfigurationClassPostProcessor
解析启动类的时候会回调AutoConfigurationImportSelector
的getAutoConfigurationEntry
方法.从而引出spring-boot-autoconfigure
模块.AopAutoConfiguration
, 隶属于spring-boot-autoconfigure
模块中的一个类,专门用来提供CglibAutoProxyConfiguration
和JdkDynamicAutoProxyConfiguration
, 这两者是实现AOP的核心类.AnnotationAwareAspectJAutoProxyCreator
.AopAutoConfiguration
顺便也通过 @import 引入了AspectJAutoProxyRegistrar
,AspectJAutoProxyRegistrar
又注册了AnnotationAwareAspectJAutoProxyCreator
,AnnotationAwareAspectJAutoProxyCreator
负责找出所有 @AspectJ修饰过的对象,为实现 AOP 做准备.
下一篇,将继续讲实现 AOP 的流程.