上一篇文章介绍了PostProcessorRegistrationDelegate
这个委托类在高层次上对于bean工厂后置处理器的调用分析,得到了一个基本结论,自定义的bean组件都是在ConfigurationClassPostProcessor
这个类处理的,我们这次来看看这个类是如何处理bean的扫描、解析与注册的。
ConfigurationClassPostProcessor类分析
/**
* Derive further bean definitions from the configuration classes in the registry.
* 从registry中的配置类导出(衍生出)更多的beanDef
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
这个方法是外部调用该类的一个极其重要的方法,旨在通过registry获取更多的bean
。方法内部最后调用了processConfigBeanDefinitions(registry)
,我们看一下
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
/**
* 该方法在springboot的启动过程中只会被调用一次。
* 最小配置下此时registry已经有7个内部配置类 + 1个springboot启动类
* @param registry
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//判断beanDef是否已被当做configuration class处理过
// 标记一下 ConfigurationClassPostProcessor
// AutowiredAnnotationBeanPostProcessor
// RequiredAnnotationBeanPostProcessor
// CommonAnnotationBeanPostProcessor
// EventListenerMethodProcessor
// DefaultEventListenerFactory
// SharedMetadataReaderFactoryContextInitializer
// 这里统一称之为A,另外springboot启动类称之为B
// A,B都不会进入该if分支
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// B会进入该分支,beanDef被判断为合格的候选组件,且被设置isFullConfigurationCandidate
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
// springboot最小配置下,只有springboot启动类一个候选配置类
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
// 按照@Order的值进行升序排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
// registry是DefaultListableBeanFactory类型是SingletonBeanRegistry接口的实现类
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// 获取注册中心的org.springframework.context.annotation.internalConfigurationBeanNameGenerator
// 因为该类目前只是一个BeanDef,还未被实例化,则返回null
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
// 构造Configuration类解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 目前的候选配置类只有springboot启动类一个配置类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 这句代码很重要,它会解析候选配置类candidates,极有可能会扫描出多个bean并注册到registry
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());
}
// 一般的Configuration配置类中会有@Bean注解的方法,其返回值也会被当做一个BeanDef,是在此处处理的
// 这句代码也很重要,会扫描出多个bean并注册到registry
this.reader.loadBeanDefinitions(configClasses);
// 已处理的配置类集合增加元素
alreadyParsed.addAll(configClasses);
// 因为基于当前的候选配置类找到的一堆配置类已经注册到registry中,所以candidate是要清空
candidates.clear();
// 方法开头已经说过springboot启动模式最小配置下candidateNames含8个元素
// 当条件满足时,说明扫描出了更多的bean组件,这些组件当然有可能再衍生出更多的bean
if (registry.getBeanDefinitionCount() > candidateNames.length) {
// registry注册中心上的beanDef被当做新的候选配置类,newCandidateNames个数一般大于alreadyParsed的个数
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
//通过循环整理出已解析的配置类的类名
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
//旧的候选配置类不包括新的候选配置类,则继续处理
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
// 当bd符合Configuration验证且已解析配置类不包含bd时,bd才可以被当做下一个候选配置类继续被解析
// while循环才会继续
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
// 注册 org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
//springboot启动时metadataReaderFactory属于ConcurrentReferenceCachingMetadataReaderFactory,不进入该分支
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
代码中的注释已经非常清楚,bean组件的扫描发现注册是以springboot启动类为起始点的。最关键的两处代码如下
parser.parse(candidates);
this.reader.loadBeanDefinitions(configClasses);
下面将通过第二节和第三节分别讲述这两行代码的作用
ConfigurationClassParser解析候选组件
parser.parse(candidates)
这行代码最终会调用到configurationClassParser.processConfigurationClass(ConfigurationClass configClass)
这里,如下
/**
* springboot启动时第一次调用该方法时configClass指代的是springboot启动类
*/
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 判断configClass是否应该跳过处理
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// springboot启动第一次进入此处时,根据configClass获取到的existingClass
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
// 递归处理配置类及其父类
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
// doProcessConfigurationClass方法有非常大的可能会再次递归到该方法
this.configurationClasses.put(configClass, configClass);
}
这个方法充满了递归思想,processConfigurationClass ===> doProcessConfigurationClass ===>… ===>processConfigurationClass,在解析配置的时候,可能会递归再执行到本方法。
最终,执行到最底层(栈的底部)时,configurationClasses记录着已经处理过的配置类,在上一章的示例中,parser.parse(candidates)
执行完后configurationClasses如下:
想要着重说明的两个类是AppConfig.java和StaffService.java
。
AppConfig.java是一个配置类,内部定义了一个@Bean方法,返回User。图中可以看到User并没有被当做configClasses的一员。而是作为appConfig这个configClass内部的要素。
StaffService.java是一个@Service类,通过@Import引入了Staff这个类,图中显示其目前是一个没有名字的configClass
这个时候,我们看看registry已经注册了哪些bean?如下:
可以明显地看出除了 User和Staff这两个bean,其他的都已经被注册了。这两个bean的注册在下一节讲。而图中方框中的bean毫无疑问是在doProcessConfigurationClass(configClass, sourceClass)
注册的。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
// 1 处理内部成员类
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
// 2 处理@PropertySource注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 3 处理@ComponentScan注解覆盖到的类
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
// 3a 获取扫描到的Configuration,Component组件,注意这些组件在方法执行过程中
// 已注册到registry
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)) {
// 3b 该方法经过一些流程会再次进入doProcessConfigurationClass方法,递归点在这里
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 4 Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 5 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);
}
}
// 6 Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 7 Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// 8 Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
图片中bean都是在代码区的3a标记处注册的。关于componentScanAnnotationParser.parse(AnnotationAttributes componentScan, final String declaringClass)
本篇文章暂时不讲。
bean的扫描处理在doProcessConfigurationClass方法中,如代码区所示,主要体现在8个方面
1)处理内部成员类
2)处理@PropertySource注解
3)处理@ComponentScan注解覆盖到的类
4)处理@Import注解
5)处理@ImportResource注解
6)处理@Bean方法注解
7)处理接口上的默认方法
8)处理父类
着重注意这个方法可能递归调用到自己
ConfigurationClassBeanDefinitionReader解读配置
回过头来,我们再看this.reader.loadBeanDefinitions(configClasses);
这行代码,刚才说过User和Staff这两个bean这两个bean就是在此处注册的。参数configClasses
在上面的图片中已经展示过。
/**
* Read {@code configurationModel}, registering bean definitions
* with the registry based on its contents.
* 通过configurationModel的内部内容去注册beanDef
*/
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
loadBeanDefinitions通过configurationModel的内部内容去注册beanDef,方法内部调用了loadBeanDefinitionsForConfigurationClass方法
/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 1 如果配置类是被引入的,如配置类是@Import注解的值,则会注册这个configClass
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 2 如果配置类内部有@Bean方法,要去注册这个方法返回的bean
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 3 如果配置类上又通过@importResource引入了配置文件,则配置文件中的bean要在这里被注册
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 4 处理配置类上通过@Import引入的实现了ImportBeanDefinitionRegistrar接口的类,这样的类被@Import引入
// 后,并不会被注册为bean。它跟registry类似,它是一个可以接收bean注册的类,像是一个bean注册中心
// 在这个接口中有一个方法registerBeanDefinitions,听名字就应该知道是干什么的了,这个方法内部可以注册
// bean到注册中心上去
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
这个方法内部对configClass的内容进行分析,对于不同的情况主要有四种不同的处理,代码区的注释已经非常清楚。对于第4点,我们可以参考这篇博客 使用@import导入实现了ImportBeanDefinitionRegistrar接口的类,不能被注册为bean,你会有更深的理解。
总结与思考
bean解析注册流程中最关键的步骤及相关类在下面的时序图有展示
ConfigurationClassPostProcessor
可以看做是整个bean扫描与注册的中枢大脑。它的两个主要过程parser.parse(candidate)
和reader.loadBeanDefinitions(configClasses)
是bean扫描注册的关键点。
读完整篇文章,你应该知道User和Staff这两个bean是在时序图的蓝线处注册的。而一般的@Service,@Controller注解的类是在绿线处注册的。
参考文章
1 使用@import导入实现了ImportBeanDefinitionRegistrar接口的类,不能被注册为bean