目录
二、SpringApplication的prepareContext
三、SpringApplication的refreshContext
前言
我们在springboot项目开发过程中,可能存在多个项目需要用到同一个功能,这时候不同的项目组实现这个功能都采用了不同的方式,同时后期需要各自项目组进行代码维护升级,这样会导致代码质量不一致、重复造轮子、浪费人力等问题。
遇到上面的问题,需要思考下此功能是否具备通用性,如第三方的接入、中间件的接入、工具类等,如果具备,那么我们可以把这一块功能抽离出来,做成公共组件,在springboot的项目中,我们就可以采用自定义starter组件的方式实现公共组件。
自定义配置实现
1、创建自定义配置starter项目,目录如下:
2、pom.xml引入maven包spring-boot-autoconfigure
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sam-spring-boot</artifactId>
<groupId>com.sam.project</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-spring-boot-starter</artifactId>
<name>my-spring-boot-starter</name>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
</dependencies>
</project>
这里要注意,如果要做成组件可以maven打包出来供其他应用引入使用,packaging要设置成jar
3、创建一个User类,因为是例子,这里就只设置一个属性name
package com.sam.project.my.domain;
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4、创建属性类:MyProperty,负责从application.properties文件中获取配置的属性值
package com.sam.project.my.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my.user")
public class MyProperty {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@ConfigurationProperties(prefix = "my.user")注解就是标识要匹配前缀是my.user的值,name对应的是my.user.name=xxxx的值
5、编写配置类:MyAutoConfiguration,这是关键类
package com.sam.project.my;
import com.sam.project.my.config.MyProperty;
import com.sam.project.my.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(MyProperty.class)
public class MyAutoConfiguration {
@Autowired
private MyProperty myProperty;
@Bean
@ConditionalOnMissingBean(User.class)
public User user(){
User user = new User();
user.setName(myProperty.getName());
return user;
}
}
- @Configuration 标识这个类是配置类,类似于spring的xml配置文件的作用;
- @EnableConfigurationProperties(MyProperty.class) 看到Enable的前缀就猜出,只有加上这个配置,才能让MyProperty的类初始化自动填充属性,下面就可以用@Autowired获取这个bean来使用
- @Bean 就是表示这个方法创建的User作为bean注入到IOC容器;
- @ConditionalOnMissingBean 表示当容器中没有User这个Bean时才执行这个方法新建一个User的Bean;
6、在resources资源文件夹下面,创建文件夹META-INF,然后创建文件spring.factories(路径是/resources/META-INF/spring.factories)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sam.project.my.MyAutoConfiguration
到此,我们就已经创建好组件了,然后使用maven执行install命令打包到本地仓库中就可以;
自定义组件测试
1、创建一个springboot的web项目,然后pom中引入上面的my-spring-boot-starter组件maven包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sam-spring-boot</artifactId>
<groupId>com.sam.project</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>web-test-spring-boot-start</artifactId>
<packaging>war</packaging>
<name>web-test-spring-boot-start</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.sam.project</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<finalName>sam-project-web</finalName>
</build>
</project>
2、编写application.properties文件
server.port=8080
server.servlet.context-path=/sam/web
my.user.name=sam
3、编写测试controller类
package com.sam.project.web.controller;
import com.sam.project.my.domain.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping(value = "/test", produces = "application/json; charset=utf-8")
public class TestController {
@Resource
private User user;
@GetMapping(value = "/name/get")
public String getUserName() {
return "hello " + user.getName();
}
}
这里就可以获取User里面的值为sam
4、编写启动类SamWebApplication
package com.sam.project.web;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SamWebApplication {
public static void main(String[] args) {
SpringApplication.run(SamWebApplication.class, args);
}
}
5、测试
启动运行后,在浏览器输入http://localhost:8080/sam/web/test/name/get
源码分析
一、启动类
先从启动类来分析如何加载组件
@SpringBootApplication
public class MyApiApplication {
public static void main(String[] args) {
SpringApplication.run(MyApiApplication.class, args);
}
}
通过jar命令启动的时候,执行run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
new SpringApplication(primarySources)执行初始化,这里进去,这里的primarySource是class com.sam.project.web.SamWebApplication,后面就会根据这个primarySource进行解析。
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
......
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
......
}
我们关注setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))里面的方法getSpringFactoriesInstances(ApplicationContextInitializer.class)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
进入方法SpringFactoriesLoader.loadFactoryNames(type, classLoader)
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
我们可以看到调用的第二个loadFactoryNames方法里面的代码classLoader.getResources("META-INF/spring.factories")会加载项目里面所有resources资源文件夹下的META-INF/spring.factories文件,然后解析spring.factories,获取里面的keyValue格式的Properties值,最后保存到Map<ClassLoader, MultiValueMap<String, String>> cache中,其中ClassLoader是用来区分加载器,MultiValueMap里面的内容我们可以看一下:
其中EnableAutoConfiguration的map里面value就有我们后续自己实现在spring.factories设置的自定义组件的初始化配置类的全路径类名称。
(注意,这里只是全量预缓存,还没有真正用到EnableAutoConfiguration里面的value列表进行实例化,目前还是执行者其中的获取ApplicationContextInitializer的value里面的类的实例化)
二、SpringApplication的prepareContext
接着就是执行new SpringApplication(primarySources).run(args)的方法中执行的prepareContext进行容器准备,一直跟到最后的load方法:
SpringApplication {
run(...) {
// 准备context
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
......
// 这里获取到了之前primarySource的值class com.sam.project.web.SamWebApplication,然后执行load加载
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
......
}
protected void load(ApplicationContext context, Object[] sources) {
......
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
}
进去到BeanDefinitionLoader这个加载BeanDefinition的类方法中,这里主要就是对SamWebApplication类型的Source进行加载
BeanDefinitionLoader {
int load() {
int count = 0;
for (Object source : this.sources) {
//class com.sam.project.web.SamWebApplication,执行load加载
count += load(source);
}
return count;
}
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
......
}
private int load(Class<?> source) {
......
if (isEligible(source)) {
// 调用AnnotatedBeanDefinitionReader的注册
this.annotatedReader.register(source);
return 1;
}
return 0;
}
}
this.annotatedReader.register(source)注册调用的就是AnnotatedBeanDefinitionReader类的注册方法,将SamWebApplication和BeanDefinition的映射注册进AnnotationConfigServletWebServerApplicationContext容器里面
AnnotatedBeanDefinitionReader {
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
// 这里解析com.sam.project.web.SamWebApplication的class获取metaData基础数据,得到BeanDefinition
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
......
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 将SamWebApplication和BeanDefinition的映射注册进AnnotationConfigServletWebServerApplicationContext里面
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
}
三、SpringApplication的refreshContext
执行完prepareContext准备工作后,接着会执行
1、refreshContext(context)刷新容器方法
SpringApplication {
run(...) {
// 1、刷新context
refreshContext(context);
}
private void refreshContext(ConfigurableApplicationContext context) {
refresh((ApplicationContext) context);
......
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
refresh((ConfigurableApplicationContext) applicationContext);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
}
2、applicationContext的refresh方法使用的是抽象类AbstractApplicationContext类中的refresh方法;
3、实例化和调用所有 BeanFactoryPostProcessor, BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它
4、getBeanFactoryPostProcessors()获取当前已加载的三个Processors:
- org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
- org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
- org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
AbstractApplicationContext {
// 2、调用到抽象类的刷新方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
......
// 3、实例化和调用所有 BeanFactoryPostProcessor, BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它
invokeBeanFactoryPostProcessors(beanFactory);
......
}
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 4、获取当前已加载的三个Processors:
//[org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor, //org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor, //org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor]
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
......
}
}
5、PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors负责按照优先级处理BeanDefinitionRegistryPostProcessors,先处理org.springframework.context.annotation.internalConfigurationAnnotationProcessor。这里第一个优先处理是currentRegistryProcessors=ConfigurationClassPostProcessor这个用于处理配置类的Processor。
6、调用ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法注册;
PostProcessorRegistrationDelegate {
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 5、按照优先级处理BeanDefinitionRegistryPostProcessors,先处理org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// 这里第一次处理是currentRegistryProcessors=ConfigurationClassPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
......
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
// 6、调用ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法注册;
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
}
7、遍历candidateNames,如果是属于自定义的@Configuration注解修饰的配置类的configCandidates(这里只有我们的启动类samWebApplication符合,其他都是系统的),就加到configCandidates中;接下来执行的就是解析每一个@Configuration注解的类,这里只有一个samWebApplication需要解析,执行方法parser.parse(candidates);
ConfigurationClassPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
......
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 7、下面的省略代码是遍历candidateNames,如果是属于自定义的@Configuration注解修饰的配置类的configCandidates(这里只有我们的启动类samWebApplication符合,其他都是系统的),就加到configCandidates中
......
// 解析每一个@Configuration注解的类,这里只有一个samWebApplication需要解析
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
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());
}
// 18、这个方法主要是把前面解析出来的配置类中的@Bean注解的方法解析成beanDefinition都注册到容器中
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
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);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
......
}
}
8、第7步得到的com.sam.project.web.SamWebApplication的BeanDefinitionHolder属于AnnotatedBeanDefinition,调用这里解析;
9、处理校验SamWebApplication的注解,并组装这个的SourceClass资源类
10、循环递归处理
11、doProcessConfigurationClass方法执行:如果是有注解@Compnent内部类,并且是配置类,就执行processConfigurationClass方法递归;
12、处理有注解@PropertySource的属性
13、处理有注解@ComponentScan
14、处理有@Import的注解
- getImports(sourceClass)遍历SamWebApplication启动资源类的所有注解,获取Import注解的资源类AutoConfigurationPackages和AutoConfigurationImportSelector
- AutoConfigurationPackages添加到SamWebApplication的ConfigurationClass类的importBeanDefinitionRegistrars中
- AutoConfigurationImportSelector实例化成ImportSelector后添加到当前的List<DeferredImportSelectorHolder>中,等待后续的处理解析;
ConfigurationClassParser {
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 8、属于AnnotatedBeanDefinition,调用这里解析,com.sam.project.web.SamWebApplication的BeanDefinitionHolder
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
......
}
}
// 17、这里是处理下面AutoConfigurationImportSelector实例化的ImportSelector,得到的就是EnableAutoConfiguration映射的配置列表
this.deferredImportSelectorHandler.process();
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
......
// 9、处理校验SamWebApplication的注解,并组装sourceClass
SourceClass sourceClass = asSourceClass(configClass, filter);
// 10、循环递归处理
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//11、如果是有注解@Compnent内部类,判断如果是配置类,就执行processConfigurationClass方法递归
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
//12、处理有注解@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.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
//13、处理有注解@ComponentScan
// 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());
}
}
}
}
//14、处理有@Import的注解
// getImports(sourceClass)遍历SamWebApplication启动资源类的所有注解,获取Import注解的资源类AutoConfigurationPackages和AutoConfigurationImportSelector
// AutoConfigurationPackages添加到SamWebApplication的ConfigurationClass类的importBeanDefinitionRegistrars中
// AutoConfigurationImportSelector实例化成ImportSelector后添加到当前的List<DeferredImportSelectorHolder>中
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
......
return null;
}
}
15、ConfigurationClassParser的processImports()方法会调用getImports执行process方法,这里的group.process就是调用AutoConfigurationImportSelector的process方法
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
16、AutoConfigurationImportSelector.java类的getAutoConfigurationEntry方法里面的方法getCandidateConfigurations获取候选配置类名称;这里loadFactoryNames方法才是从前面说的Map<ClassLoader, MultiValueMap<String, String>> cache里面获取EnableAutoConfiguration.class对应的配置列表;
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 16、调用getCandidateConfigurations方法获取配置类名称列表
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
17、执行完上面后,继续执行ConfigurationClassParser类parse()方法的this.deferredImportSelectorHandler.process(),这里是处理上面AutoConfigurationImportSelector实例化的ImportSelector,得到的就是EnableAutoConfiguration映射的配置列表,包含了自定义的MyAutoConfiguration;
18、执行完ConfigurationClassParser.parse(...)方法后,ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法继续执行到this.reader.loadBeanDefinitions(configClasses),这个方法的作用就是把前面解析出来的配置类中的@Bean注解的方法解析成beanDefinition都注册到容器中。
结尾:到这里,已经把自定义Configuration的类进行解析,对@Bean等配置类里面的资源都解析成BeanDefinition注册到容器中;然后等到执行finishBeanFactoryInitialization(beanFactory)方法的时候就进行bean的初始化。