SpringBoot現(xiàn)在基本是標(biāo)配了晕翠,除非是老舊的項(xiàng)目盖文,或者保守一點(diǎn)的企業(yè),都會(huì)選擇SpringBoot双饥。
在SpringBoot出現(xiàn)之前媒抠,使用SSH或者SSM,都要很多的XML配置文件咏花,而這些配置趴生,在每個(gè)項(xiàng)目中阀趴,大部分都是相同的。
雖然都一樣苍匆,但項(xiàng)目都要配置刘急,可能會(huì)出現(xiàn)配置幾小時(shí),寫代碼幾分鐘的情況浸踩,把項(xiàng)目啟動(dòng)拖慢了叔汁。SpringBoot則是為了解決這種問題而生的,提高開發(fā)效率检碗。
用過SpringBoot的小伙伴都知道据块,在IDEA使用SpringBoot Initializer,快速配置項(xiàng)目折剃,寫一個(gè)Controller就可以另假,快速搭建起Web項(xiàng)目。
SpringBoot給我們提供了大量的starter怕犁,里面已經(jīng)幫我們配置了常用配置边篮,如果我們需要改動(dòng),則在application.yml中配置即可奏甫。
SpringBoot之所以可以這樣做苟耻,是因?yàn)樗脑O(shè)計(jì)策略,開箱即用和約定大于配置扶檐。
下面我們看下SpringBoot幫我們做了什么吧凶杖!
自動(dòng)裝配
要使用SpringBoot,我們需要指定parent父工程
基礎(chǔ)配置
pom文件指定parent父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
點(diǎn)進(jìn)去會(huì)發(fā)現(xiàn)款筑,spring-boot-starter-parent也有父工程智蝠,就是spring-boot-dependencies
,繼續(xù)點(diǎn)進(jìn)去
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
spring-boot-dependencies
看來是管理依賴和版本號(hào)的奈梳,所以我們依賴第三方庫(kù)時(shí)杈湾,如果在這個(gè)依賴列表中有,則不需要寫版本號(hào)了
<properties>
<activemq.version>5.15.10</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.76</appengine-sdk.version>
<artemis.version>2.10.1</artemis.version>
<aspectj.version>1.9.4</aspectj.version>
<assertj.version>3.13.2</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.1</awaitility.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.10.1</byte-buddy.version>
...太多了攘须,省略其他
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependencies>
...太多了漆撞,省略其他
<dependencyManagement>
我們的父工程spring-boot-starter-parent
,還幫我們指定了配置文件的格式
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources</directory>
<!-- 指定了配置文件的格式于宙,加載順序?yàn)閥ml => yaml => properties -->
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<excludes>
<exclude>**/application*.yml</exclude>
<exclude>**/application*.yaml</exclude>
<exclude>**/application*.properties</exclude>
</excludes>
</resource>
</resources>
<build>
啟動(dòng)器 starter
SpringBoot將每種使用場(chǎng)景所需要的依賴和依賴浮驳,封裝成一個(gè)啟動(dòng)器starter,我們需要引入某種領(lǐng)域的功能時(shí)捞魁,直接依賴對(duì)應(yīng)的starer即可至会。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
例如我們常用的Web開發(fā),需要依賴SpringMVC等谱俭,SpringBoot提供了spring-boot-starter-web
啟動(dòng)器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
我們點(diǎn)進(jìn)行該starter奉件,他給我們定義了以下依賴:
- spring-boot-starter SpringBoot基礎(chǔ)啟動(dòng)器
- spring-boot-starter-json json序列化宵蛀、反序列化的啟動(dòng)器
- spring-boot-starter-tomcat 內(nèi)嵌Tomcat
- spring-web和spring-webmvc,就是我們的SpringMVC
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.2.0.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>tomcat-embed-el</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
啟動(dòng)類 SpringBootApplication
SpringBoot要求我們提供一個(gè)啟動(dòng)類县貌,并且類頭加上 @SpringBootApplication注解术陶,該注解就是SpringBoot啟動(dòng)的核心。
@SpringBootApplication
public class SpringbootEsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootEsApplication.class, args);
log.info("項(xiàng)目啟動(dòng)成功煤痕,訪問地址:http://localhost:8081/");
}
}
我們點(diǎn)進(jìn)去@SpringBootApplication
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
//省略屬性...
}
我們會(huì)發(fā)現(xiàn)SpringBootApplication是一個(gè)復(fù)合注解瞳别,當(dāng)中最重要的是@SpringBootConfiguration
和@EnableAutoConfiguration
,這2個(gè)注解杭攻。
@ComponentScan注解是包掃描祟敛,因?yàn)闆]有配置掃描包,默認(rèn)是掃描標(biāo)識(shí)該注解的類的包兆解,以及它以下的子包馆铁,所以啟動(dòng)類一般在根包下。
- @SpringBootConfiguration注解
我們發(fā)現(xiàn)@SpringBootConfiguration
注解 锅睛,最主要是加上了@Configuration
注解埠巨。
我們知道@Configuration
注解 就代表了一個(gè)JavaConfig方式的Spring的容器,所以我們啟動(dòng)器類也相當(dāng)于一個(gè)容器现拒。
SpringBootConfiguration
注解沒什么可看了辣垒,我們看下一個(gè)注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(
proxyBeanMethods = false
)
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
- @EnableAutoConfiguration注解
@EnableAutoConfiguration
注解中,主要注解是@Import(AutoConfigurationImportSelector.class)
印蔬。
@Import注解勋桶,幫我們導(dǎo)入了AutoConfigurationImportSelector
這個(gè)類
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- AutoConfigurationImportSelector類
AutoConfigurationImportSelector類實(shí)現(xiàn)了DeferredImportSelector
接口,該接口繼承ImportSelector
接口 侥猬,會(huì)要求復(fù)寫selectImports()
方法例驹。
ImportSelector
接口,主要是為了導(dǎo)入@Configuration
配置的退唠,而DeferredImportSelector
是延期導(dǎo)入鹃锈,當(dāng)所有的@Configuration
都處理完成后,再調(diào)用DeferredImportSelector
進(jìn)行處理瞧预。
所以AutoConfigurationImportSelector
類是延遲導(dǎo)入的屎债,所有@Configuration
都處理完后,再調(diào)用它的selectImports()
方法垢油。
selectImports()
方法盆驹,調(diào)用了getAutoConfigurationEntry()
方法,而getAutoConfigurationEntry()
又調(diào)用了getCandidateConfigurations()
方法秸苗。
而getCandidateConfigurations()
方法是重點(diǎn)召娜!
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
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;
}
}
- getCandidateConfigurations()方法
方法中运褪,調(diào)用SpringFactoriesLoader.loadFactoryNames()惊楼,傳入2個(gè)參數(shù)玖瘸,EnableAutoConfiguration的Class和Bean的ClassLoader。
loadFactoryNames()
方法檀咙,返回一個(gè)集合雅倒,如果集合為空,進(jìn)入下一句的Assert斷言弧可,就會(huì)拋出異常蔑匣。
最后返回這個(gè)配置集合。
//AutoConfigurationImportSelector
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;
}
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
- SpringFactoriesLoader
loadFactoryNames()
方法棕诵,獲取了傳入的EnableAutoConfiguration
注解的Class裁良,調(diào)用loadSpringFactories()方法。
而loadSpringFactories()
方法校套,會(huì)讀取jar包中META-INF
目錄的spring.factories
配置文件价脾。
如果讀取不到,則返回一個(gè)空集合笛匙。
public final class SpringFactoriesLoader {
//jar包中的META-INF目錄下侨把,spring.factories配置文件
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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);
}
}
}
- spring.factories配置文件
我們選一個(gè)starter,例如spring-boot-autoconfigure妹孙,找到它的META-INF目錄秋柄,找到spring.factories文件,打開蠢正。
我們發(fā)現(xiàn)文件里骇笔,配置了很多自動(dòng)配置屬性(內(nèi)容有刪減,實(shí)在太多了O浮)蜘拉。
它的形式是Key-Value,例如其中一個(gè)Key是EnableAutoConfiguration的全類名有鹿,它的Value是好幾個(gè)名字以AutoConfiguration結(jié)尾的類旭旭,每個(gè)類之間用逗號(hào)分隔。
剛才我們跟蹤的loadFactoryNames()
方法葱跋,傳入的EnableAutoConfiguration的Class持寄,就是要從spring.factories
配置文件中找到它對(duì)應(yīng)的那一組Value。
我們以ServletWebServerFactoryAutoConfiguration
為例娱俺,點(diǎn)進(jìn)去看一下
# 省略其他配置...
# Auto Configure !!!!!!!! 重點(diǎn)在這里 !!!!!!!!
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
# 省略其他配置...
- ServletWebServerFactoryAutoConfiguration類
我們看到該類的類頭上稍味,有@EnableConfigurationProperties
注解,該屬性表示加載配置屬性荠卷,這里指定了一個(gè)ServerProperties
類模庐。
我們點(diǎn)進(jìn)去ServerProperties
類看一下
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
//...省略
}
- ServerProperties類
這個(gè)是一個(gè)和配置信息相對(duì)應(yīng)的類,它類頭上配置了@ConfigurationProperties
注解油宜,它可以將配置文件中的配置項(xiàng)的內(nèi)容掂碱,映射到我們的類的變量上怜姿。
注解上,配置的prefix屬性疼燥,就代表了server.xxx系列配置沧卢,例如我們配置端口:server.port,該注解將我們的配置映射到ServerProperties
上醉者。
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
}
到此為止但狭,自動(dòng)配置的流程基本通了,總結(jié)一下:
SpringBoot啟動(dòng)類的main方法啟動(dòng)時(shí)撬即,會(huì)找@EnableAutoConfiguration注解立磁,而該注解就在@SpringBootApplication上。而@EnableAutoConfiguration注解上剥槐,使用了@Import注解息罗,導(dǎo)入了AutoConfigurationImportSelector類。
而該類才沧,會(huì)去找META-INF/spring.factories配置文件迈喉,這個(gè)配置文件中配置了一系列的以AutoConfiguration結(jié)尾的類,就是自動(dòng)配置類温圆。
而每個(gè)配置類挨摸,都有一個(gè)Properties結(jié)尾的配置類,它和我們?cè)趛ml中的配置項(xiàng)時(shí)一一對(duì)應(yīng)的岁歉,相當(dāng)于綁定配置到了該對(duì)象中得运。
如果只是想面試了解一下,到這里就可以了锅移,而如果更想深入熔掺,就要繼續(xù)跟一下。
如果要繼續(xù)跟非剃,就還有一個(gè)疑點(diǎn)置逻,自動(dòng)裝配是什么時(shí)候開始的呢,其實(shí)就是AutoConfigurationImportSelector
類上的selectImports()
方法备绽,還不知道它什么會(huì)被調(diào)用券坞。
何時(shí)開始進(jìn)行自動(dòng)裝配
我們回歸到Spring,Spring應(yīng)用啟動(dòng)肺素,會(huì)在AbstractApplicationContext
類中恨锚,調(diào)用refresh()
方法。
refresh()
方法中倍靡,調(diào)用了invokeBeanFactoryPostProcessors()
方法猴伶,該方法是用來處理BeanFactoryPostProcessor
接口的,而BeanFactoryPostProcessor
的有一個(gè)子接口BeanDefinitionRegistryPostProcessor
。
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
//省略無關(guān)代碼...
invokeBeanFactoryPostProcessors(beanFactory);
//省略無關(guān)代碼...
}
}
//BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
- ConfigurationClassPostProcessor類
子接口BeanDefinitionRegistryPostProcessor他挎,有一個(gè)實(shí)現(xiàn)類ConfigurationClassPostProcessor
筝尾,它是專門處理@Configuration
注解的。
processConfigBeanDefinitions()
方法中雇盖,就是處理@Configuration
注解的類忿等。主要是使用ConfigurationClassParser
類的parse()
方法栖忠。
我們進(jìn)去parse()
方法崔挖,看一下
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//省略部分代碼
// Parse each @Configuration class
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 {
//解析處理@Configuration注解的類
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());
}
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;
}
}
while (!candidates.isEmpty());
}
}
- ConfigurationClassParser類的parse()方法
首先類中有一個(gè)內(nèi)部類DeferredImportSelectorHandler
,構(gòu)造方法ConfigurationClassParser
實(shí)例時(shí)庵寞,就創(chuàng)建該內(nèi)部類的實(shí)例狸相。
在parse()
方法調(diào)用時(shí),最后一句調(diào)用了processDeferredImportSelectors()
方法捐川。
class ConfigurationClassParser {
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 (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
//重點(diǎn)
processDeferredImportSelectors();
}
}
- processDeferredImportSelectors()方法
重點(diǎn)在String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
脓鹃。
調(diào)用的是DeferredImportSelectorHolder
類,它保存了DeferredImportSelector
的引用古沥,在這個(gè)for循環(huán)中瘸右,調(diào)用了DeferredImportSelector
的selectImports()
方法,從而調(diào)用到了我們之前分析的AutoConfigurationImportSelector
類中的selectImports()
方法了岩齿。
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);
}
}
}
//該類太颤,保存了配置類和DeferredImportSelector的引用
private static class DeferredImportSelectorHolder {
private final ConfigurationClass configurationClass;
private final DeferredImportSelector importSelector;
public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
this.configurationClass = configClass;
this.importSelector = selector;
}
public ConfigurationClass getConfigurationClass() {
return this.configurationClass;
}
public DeferredImportSelector getImportSelector() {
return this.importSelector;
}
}
參考資料
SpringBoot:認(rèn)認(rèn)真真梳理一遍自動(dòng)裝配原理