SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析
作者:互联网
先从@SpringBootApplication
开始。
在启动流程章节中,我们讲述了SpringBoot2大致的启动步骤,并进行了源码详解。但是在刷新容器这块并未展开,refreshContext(context);
简单的一行代码,背后却做了太多事情。所以为了不喧宾夺主,本篇也尽量选取和注解@SpringBootApplication
有关的方法讲解。
1)springBoot启动类加载
首先加载springBoot启动类注入到spring容器中bean map中,看下prepareContext方法中的load方法:
load(context, sources.toArray(new Object[0]));
跟进该方法最终会执行BeanDefinitionLoader
的load
方法:
private int load(Object source) { Assert.notNull(source, "Source must not be null"); //如果是class类型,启用注解类型 if (source instanceof Class<?>) { return load((Class<?>) source); } //如果是resource类型,启用xml解析 if (source instanceof Resource) { return load((Resource) source); } //如果是package类型,启用扫描包,例如:@ComponentScan if (source instanceof Package) { return load((Package) source); } //如果是字符串类型,直接加载 if (source instanceof CharSequence) { return load((CharSequence) source); } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }
继续跟进
load(Class<?> source)
方法:上述方法判断启动类中是否包含
@component
注解,可我们的启动类并没有该注解。继续跟进会发现,AnnotationUtils
判断是否包含该注解是通过递归实现,注解上的注解若包含指定类型也是可以的。
启动类中包含@SpringBootApplication
注解,进一步查找到@SpringBootConfiguration
注解,然后查找到@Component
注解,最后会查找到@Component
注解:@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { }
在查找到
@Component
注解后,表面该对象为spring bean,然后会将其信息包装成 beanDefinitaion ,添加到容器的 beanDefinitionMap中。如下:
如此一来,我们的启动类就被包装成AnnotatedGenericBeanDefinition
了,后续启动类的处理都基于该对象了。
2)自动装配的入口:
从刷新容器开始:
public void refresh() throws BeansException, IllegalStateException { //... invokeBeanFactoryPostProcessors(beanFactory); //... }
上述省去了不相关的代码,继续跟进invokeBeanFactoryPostProcessors
方法:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { //开始执行beanFactoryPostProcessor对应实现类 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }
首先我们要知道beanFactoryPostProcessor
接口是spring的扩展接口,从名字也可以看出,是 beanFactory的扩展接口。在刷新容器之前,该接口可用来修改bean元数据信息。具体实现方式,我们继续跟着上述执行逻辑便知。
继续跟进上面invokeBeanFactoryPostProcessors
方法,第一行很关键:
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
一个比较核心的代理类出现了,AbstractApplicationContext委托执行post processors任务的工具类。
而在项目启动时会委托什么任务呢?
或许你还记得第一篇博客中介绍的SpringApplication类中applyInitializers(context);
方法吧,它会将三个默认的内部类加入到 spring 容器DefaultListableBeanFactory
中,如下:
//设置配置警告
ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
来看一下具体任务执行细节,跟进invokeBeanFactoryPostProcessors
方法:
if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
//这里开始遍历上面三个内部类,如果属于BeanDefinitionRegistryPostProcessor 子类, //加入到bean注册的集合,否则加入到 regularPostProcessors中,从名字可以看出是有规律集合。 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); //首先执行类型为PriorityOrdered的BeanDefinitionRegistryPostProcessor //PriorityOrdered类型表明为优先执行 for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { //获取对应的bean currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); //用来存储已经执行过的`BeanDefinitionRegistryPostProcessor` processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); //其次执行类型为Ordered的BeanDefinitionRegistryPostProcessor //Ordered表明按顺序执行 for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; //循环中执行类型不为PriorityOrdered,Ordered类型的BeanDefinitionRegistryPostProcessor while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); } // Now, invoke the postProcessBeanFactory callback of all processors handled so far. //执行父类方法,优先执行注册处理类 invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); //执行有规则处理类 invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
来分析一下核心代码:
String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
这行代码通过类型BeanDefinitionRegistryPostProcessor
获取的处理类名称为:
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
而在源码中却搜不到internalConfigurationAnnotationProcessor
类,为什么呢?最初看这块代码确实迷惑了半天。
在第一篇博客中,当启动springBoot,创建springBoot容器上下文AnnotationConfigEmbeddedWebApplicationContext
时,会装配几个默认bean:
public AnnotationConfigEmbeddedWebApplicationContext() {
//在这里装配
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
继续跟进会执行registerAnnotationConfigProcessors
方法:
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
//将 internalConfigurationAnnotationProcessor 对应的类包装成 RootBeanDefinition 加载到容器 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)); }
到这里,答案清晰浮现。internalConfigurationAnnotationProcessor
为bean名称,容器中真正的类则是ConfigurationClassPostProcessor
。
继续后面流程,获取ConfigurationClassPostProcessor
后,开始执行BeanDefinitionRegistryPostProcessor
:
//开始执行装配逻辑
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
3)开始执行自动配置逻辑(启动类指定的配置,非默认配置):
如上如:首先获得ConfigurationClassParser
,这个是所有配置类的解析类,比较核心。所有的解析逻辑在parser.parse(candidates);
中,我们详细的来看一下:
public void parse(Set<BeanDefinitionHolder> configCandidates) { this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { //是否是注解类 if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Exception ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } //执行配置类 processDeferredImportSelectors(); }
继续跟进parse
方法:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { //...省略不核心代码 // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { //循环处理bean,如果有父类,则处理父类。直至结束。 sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
this.configurationClasses.put(configClass, configClass); }
继续跟进doProcessConfigurationClass
方法,该方法可以说是 spring 框架支持注解配置的核心逻辑了,来看看:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { //处理内部类逻辑,由于传来的参数是我们的启动类,不含内部类,所以跳过。 processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations //针对属性配置的解析 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"); } } //这里是根据启动类 @ComponentScan 注解来扫描项目中的bean AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class); if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { // 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 necessary //遍历我们项目中的bean,如果是注解定义的bean,则进一步解析 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { //判断是否是注解bean if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) { //这里是关键,递归解析。所有的bean,如果有注解,会进一步解析注解中包含的bean parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } // Process any @Import annotations //这里又是一个递归解析,获取导入的配置类。很多情况下,导入的配置类中会同样包含导入类注解。 processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations //解析导入的 xml 配置类 if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // 获取接口中的默认方法,1.8以上的处理逻辑 for (SourceClass ifc : sourceClass.getInterfaces()) { beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { if (!methodMetadata.isAbstract()) { // A default method or other concrete method on a Java 8+ interface... configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } } } // Process superclass, if any //如果该类有父类,则继续返回。上层方法判断不为空,则继续递归执行。 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (!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 //递归实现,superclass为空,则结束递归中的循环 return null; }
来看一下获取导入配置类的逻辑:
processImports(configClass, sourceClass, getImports(sourceClass), true);
跟进
getImports
方法:
可以看到我自定义的bean以导入的方式被加载进去了。另外processImports
方法执行逻辑和上述parse
方法类似,同样采用递归处理,这里就不做展开了。
4)开始执行 SpringBoot 默认配置逻辑
继续回到
ConfigurationClassParser
中的parse
方法,回到该方法的最后一步:public void parse(Set<BeanDefinitionHolder> configCandidates) { //... //开始执行默认配置 processDeferredImportSelectors(); }
继续跟进该方法
processDeferredImportSelectors
:private void processDeferredImportSelectors() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) { ConfigurationClass configClass = deferredImport.getConfigurationClass(); try { //获取配置类 String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata()); //再次递归解析配置类 processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } } }
getImportSelector()
方法获取的 selector对象为EnableAutoConfigurationImportSelector
,继续跟进该对象的selectImports
方法:
@Override public String[] selectImports(AnnotationMetadata metadata) { try { AnnotationAttributes attributes = getAttributes(metadata); //获取默认配置类 List<String> configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } }
这里的处理方式,前面的博客中已经详细介绍过了,通过
class
类型来获取spring.factories
中的指定类,class
类型为:EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
springBoot
为我们提供的所有配置类如下,大概100多个:
在获取到
springBoot
提供的配置后,再次调用processImports
方法进行递归解析,根据我们自定义的配置文件,进行选择性配置。这么多的配置类,不可能全部进行加载,项目也用不了这么多。选择的规则是什么呢?
后续会有完整博文详细介绍。
标签:configClass,beanFactory,SpringBootApplication,源码,SpringBoot2,BeanDefinitionRegis 来源: https://blog.csdn.net/u014513171/article/details/93210617