前言
本來(lái)這篇文章去年就應(yīng)該發(fā)了,但是有些事情被耽擱了,最近整理了下發(fā)了出來(lái),關(guān)于spring源碼相關(guān)有想溝通的小伙伴可以隨時(shí)聯(lián)系我。
本篇文章內(nèi)容有點(diǎn)臃腫,可以選擇性跳躍去看。干貨還是有點(diǎn)的,尤其總結(jié)部分。
本篇承接上篇末尾之討論spring源碼案例分析之健康檢查,有興趣的讀者可以去看看,說(shuō)不定有對(duì)你幫助的驚喜。
本篇開始之前,我們先看下我項(xiàng)目總體的結(jié)構(gòu),如下圖
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxq</groupId>
<artifactId>spring-source-analysis</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
pom文件很簡(jiǎn)單,引入父工程spring-boot-starter-parent,因?yàn)檫@次我們分析借助于web切入點(diǎn),因此我們只簡(jiǎn)單引入一個(gè)starter-web就夠了,其他沒什么可說(shuō)的,再看下代碼結(jié)構(gòu):

很簡(jiǎn)單,一個(gè)springboot啟動(dòng)類,我們看下面一段代碼:
@Configuration
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
public class WebClientAutoConfiguration {
@Configuration
@ConditionalOnClass(RestTemplate.class)
public static class RestTemplateConfiguration {
private final ObjectProvider<HttpMessageConverters> messageConverters;
private final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers;
public RestTemplateConfiguration(
ObjectProvider<HttpMessageConverters> messageConverters,
ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
this.messageConverters = messageConverters;
this.restTemplateCustomizers = restTemplateCustomizers;
}
@Bean
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder builder = new RestTemplateBuilder();
HttpMessageConverters converters = this.messageConverters.getIfUnique();
if (converters != null) {
builder = builder.messageConverters(converters.getConverters());
}
List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers
.getIfAvailable();
if (!CollectionUtils.isEmpty(customizers)) {
customizers = new ArrayList<RestTemplateCustomizer>(customizers);
AnnotationAwareOrderComparator.sort(customizers);
builder = builder.customizers(customizers);
}
return builder;
}
}
}
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration.RestTemplateConfiguration#restTemplateBuilder
這段代碼spring自動(dòng)裝配了一個(gè)RestTemplateBuilder,我們注意到一個(gè)注解,@ConditionalOnMissingBean,這里我大致說(shuō)下作用,不做深入研究,以后有機(jī)會(huì)我會(huì)單獨(dú)寫篇文章去分析這個(gè)注解,這個(gè)注解的大致作用就是如果容器中沒有這個(gè)RestTemplateBuilder,那么這個(gè)@Bean的配置就生效,簡(jiǎn)單來(lái)說(shuō),就是沒有這個(gè)bean,spring就會(huì)默認(rèn)生成一個(gè),有的話就不生成,這個(gè)注解是spring自動(dòng)裝配的一個(gè)核心點(diǎn)。
我們看下面這段代碼:
@Configuration
public class Appconfig {
@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder();
}
}
這段代碼,注入了一個(gè)RestTemplateBuilder,那么問(wèn)題來(lái)了,如果我也寫了這樣一個(gè)注入方法,那么spring用的RestTemplateBuilder到底是我們的還是spring自己的,有的人說(shuō)是我們自己的,有的人說(shuō)是spring默認(rèn)的,那么,原因呢?下面我們來(lái)看下到底使用的是哪個(gè)。

獲取容器中的bean

可以看到容器中的bean的內(nèi)存地址和自定義的內(nèi)存地址是一致的,所以可以確定的是,spring確實(shí)初始化的是我們定義的的,我們稍后我具體分析這個(gè)流程。
在正式開始分析之前,我們先區(qū)分下倆個(gè)注解,這對(duì)我們下面的分析有點(diǎn)鋪墊,@Order和@Primary。
- @Order一般用于攔截器和過(guò)濾器,是控制同類型bean的執(zhí)行順序,一個(gè)容器可以有多個(gè)filter和interceptor,每個(gè)承擔(dān)不同的角色,但是可能有的先執(zhí)行,有的要后執(zhí)行,這時(shí)候@Order就排上用場(chǎng)了,在ssm架構(gòu)體系下,如果想要控制過(guò)濾器的執(zhí)行順序,是通過(guò)在web.xml中配置過(guò)濾器的順序來(lái)實(shí)現(xiàn)的,如今使用springboot方式,已經(jīng)沒有了web.xml,所以spring就創(chuàng)造了這種方式,不過(guò)現(xiàn)在已經(jīng)不僅僅局限于過(guò)濾器和攔截器,包括spring command以及其他使用場(chǎng)景。
- @Primary,是在bean注入的時(shí)候起作用的。如果一個(gè)容器中,有多個(gè)類型一樣bean,不使用相關(guān)注解去處理,那么容器初始化的時(shí)候并不會(huì)報(bào)錯(cuò),但是在注入的時(shí)候就會(huì)報(bào)錯(cuò),這是由于spring并不知道你想要使用的是哪個(gè)。我們看下面的代碼。
@Configuration
public class Appconfig {
@Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
System.out.println(restTemplateBuilder.hashCode());
return restTemplateBuilder;
}
@Bean
public RestTemplateBuilder restTemplateBuilder1() {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
System.out.println(restTemplateBuilder.hashCode());
return restTemplateBuilder;
}
}
上面這段代碼,在容器中配置了倆個(gè)RestTemplateBuilder類型的bean,不過(guò)他們的beanName一個(gè)是restTemplateBuilder,一個(gè)是restTemplateBuilder1。這樣直接去啟動(dòng)工程是不會(huì)報(bào)錯(cuò)的。


我們可以看到容器中有倆個(gè)restTemplateBuilder,這時(shí)候正常啟動(dòng),當(dāng)我加入下面這段代碼:
@Autowired
RestTemplateBuilder templateBuilder;
spring就會(huì)報(bào)出如下錯(cuò)誤:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field templateBuilder in com.xxq.web.MyApplicationContextAwaer required a single bean, but 2 were found:
- restTemplateBuilder: defined by method 'restTemplateBuilder' in class path resource [com/xxq/appconfig/Appconfig.class]
- restTemplateBuilder1: defined by method 'restTemplateBuilder1' in class path resource [com/xxq/appconfig/Appconfig.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
異常大致翻譯:找到了倆個(gè)bean,無(wú)法確定使用哪個(gè),建議我們可以使用@Primary或者@Qualifier。于是我試著加入@Primary
@Bean
@Primary
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
System.out.println(restTemplateBuilder.hashCode());
return restTemplateBuilder;
}


可以看到,spring注入的就是我們加上@Primary注解的bean。
舉這么個(gè)例子是想告訴大家,@Primary不會(huì)改變spring容器中的bean的初始化優(yōu)先級(jí),只會(huì)改變注入時(shí)候的優(yōu)先級(jí),這里牽扯到spring容器的概念,springbean容器雖然是一個(gè)map,但是不是普通意義上的map,只要你的beanName不一樣,那么都會(huì)放入到這個(gè)map中,在同一個(gè)config中的@Bean模式下,如果你定義了倆個(gè)beanName一樣的bean,無(wú)論類型,只會(huì)初始化一個(gè)到容器中,至于是哪個(gè),spring會(huì)以在appconfig類中最先讀到的那個(gè)為準(zhǔn)進(jìn)行覆蓋。如果是在不同的config中配置,name一致就算類型不一致,會(huì)使用最后讀取到的。這一塊內(nèi)容也還是比較復(fù)雜,場(chǎng)景略微有點(diǎn)多,且處理的方式也都不一樣,后續(xù)有時(shí)間再解析。
這時(shí)候就有小伙伴問(wèn)了(其實(shí)是我自己),既然上面你說(shuō)spring先讀到那個(gè)就初始化哪個(gè)這種情況,為什么就不可能先讀到spring框架默認(rèn)的bean,然后初始化呢,?接下來(lái),我們開始分析。
1. 首先第一步,我們分析springboot啟動(dòng)類的加載
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
public static ConfigurableApplicationContext run(Object source, String... args) {
// 重點(diǎn)部分,這里把啟動(dòng)類作為class類型傳遞給source變量,后續(xù)會(huì)用到這個(gè)。
return run(new Object[] { source }, args);
}
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
// 重點(diǎn)代碼,這里會(huì)進(jìn)行容器的初始化工作
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//重點(diǎn)代碼,后續(xù)會(huì)分析到
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
上面代碼是spring啟動(dòng)類的核心代碼部份,我們這次只關(guān)心關(guān)于我們研究的部分:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 重點(diǎn)方法,這里會(huì)進(jìn)行啟動(dòng)類的加載
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
看上面的倒數(shù)第二行代碼,這邊把sources作為一個(gè)對(duì)象數(shù)組傳遞進(jìn)去了,這個(gè)sources是來(lái)自于getSources方法,這個(gè)最終還是我上面提到的來(lái)源于啟動(dòng)類的傳參。
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(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();
}
private int load(Class<?> source) {
if (isGroovyPresent()) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
GroovyBeanDefinitionSource.class);
load(loader);
}
}
if (isComponent(source)) {
//重點(diǎn)部份,此處會(huì)把啟動(dòng)類進(jìn)行加載進(jìn)spring容器
this.annotatedReader.register(source);
return 1;
}
return 0;
}
private boolean isComponent(Class<?> type) {
// This has to be a bit of a guess. The only way to be sure that this type is
// eligible is to make a bean definition out of it and try to instantiate it.
// 一般都會(huì)走這個(gè)分支,因?yàn)閟pringboot啟動(dòng)類似加上這個(gè)@SpringBootApplication注解
// 就算不走這個(gè)分支,下面的分支也絕大數(shù)多情況不會(huì)走,最終默認(rèn)還是返回true
if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
return true;
}
// Nested anonymous classes are not eligible for registration, nor are groovy
// closures
if (type.getName().matches(".*\\$_.*closure.*") || type.isAnonymousClass()
|| type.getConstructors() == null || type.getConstructors().length == 0) {
return false;
}
return true;
}
上面的load方法最終會(huì)調(diào)用annotatedReader.register(source)方法,
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 此處會(huì)將啟動(dòng)類注冊(cè)進(jìn)容器的beanDefinitionMap中
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
上面最后一行方法,就是將啟動(dòng)類加載進(jìn)容器中,這里僅僅是beanDefinition的加載,還沒有到真正初始化的階段。
2. 首先第一步,我們簡(jiǎn)單分析spring容器的生命周期
refreshContext(context);
上面有提到過(guò)這個(gè)方法很重要,我們現(xiàn)在來(lái)分析。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
// 重點(diǎn)代碼,后置處理器的調(diào)用
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
我相信對(duì)spring如果有稍微了解的話,對(duì)于上述核心代碼應(yīng)該都比較熟悉,上述代碼算得上是spring最最最最核心的代碼了,以后有時(shí)間我們?nèi)慷挤治鲆贿?,這次我們重點(diǎn)分析這段代碼
invokeBeanFactoryPostProcessors(beanFactory);
分析spring源碼,要記住最關(guān)鍵的一點(diǎn)是,spring永遠(yuǎn)都是分倆步走的:
1. 將所有要初始化的bean信息放入到beanDefinitionMap中。
2. 遍歷掃描上面的map,進(jìn)行bean的實(shí)例化以及初始化操作。
spring容器的構(gòu)建幾乎所有的操作都是圍繞上述倆步來(lái)處理的,大家可以想想為什么要這么去做,我直接生成一個(gè)beanDefinition初始化一個(gè)bean難道不行嗎?
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
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()));
}
}
第一行代碼是最重要的,看方法名就知道這里可能會(huì)調(diào)用后置處理器。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<String>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<BeanDefinitionRegistryPostProcessor>();
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<BeanDefinitionRegistryPostProcessor>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
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);
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;
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);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
這一段代碼是非常的長(zhǎng),而且也非常的復(fù)雜,沒有認(rèn)真分析過(guò)這段的,這里一時(shí)半會(huì)也無(wú)法說(shuō)清,總而言之,這段代碼就是spring進(jìn)行分類整合,把所有的后置處理器進(jìn)行區(qū)分成以下幾種
- 掃描BeanDefinitionRegistry的所有bean
1. 使用BeanDefinitionRegistryPostProcessor應(yīng)用于對(duì)BeanDefinitionRegistry對(duì)bean的信息進(jìn)行調(diào)整和解析 - 調(diào)用所有后置處理器(BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor)
1. 應(yīng)用于BeanDefinitionRegistry的后置處理器
2. 應(yīng)用于BeanFactory的后置處理器
3. 以及PriorityOrdered, Ordered排序執(zhí)行的后置處理器
在我看來(lái)這倆步最大的區(qū)別就是,第一步是把所有外部的類加載解析到spring容器中,在第一步執(zhí)行前,spring容器中幾乎沒有什么bean,除了手動(dòng)放進(jìn)去的,第二步處理的就是第一步處理完的spring容器。也就是說(shuō)第一步是基礎(chǔ),第二步是升華。因此對(duì)于本文的探究點(diǎn),顯而易見是第一步的解析,我們的bean為什么能優(yōu)先于spring默認(rèn)的,很大概率是在第一步已經(jīng)處理好的。

上述截圖,是容器第一次執(zhí)行后置處理器的地方
以下分析 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

可以看到除了第六個(gè)是我們程序自定義的,其他都是spring默認(rèn)的,也就是說(shuō),到這一步容器就這7個(gè)bean。然后看263行代碼,這邊有個(gè)判斷,我們看下具體作用是什么。



這邊有個(gè)spring得fullConfiguration以及l(fā)iteConfigurationClass一說(shuō),一般來(lái)說(shuō)一個(gè)springbean要么是full或者lite,經(jīng)過(guò)上面的過(guò)濾后,只剩下我們程序得啟動(dòng)類符合了,其中具體邏輯可以自行去查看。

上圖第388最終調(diào)用到下面方法塊

第一部分 主要解析當(dāng)前類得內(nèi)部類
第二部分 主要解析一些propertySource
第三部分 以當(dāng)前類為基類掃描包下面得所有類
首先他會(huì)判斷我們啟動(dòng)類上有沒有componentScan注解,有的話進(jìn)行獲取參數(shù)。

我們可以看到springbootApplication注解默認(rèn)包含了ComponentScan注解,因此是可以解析到的。

在第289行,對(duì)這個(gè)componentScan進(jìn)行掃描,如果這個(gè)注解value為null,那么就直接獲取當(dāng)前類得所在包名進(jìn)行掃描,而我們所定義的倆個(gè)restTemplateBuild就是在當(dāng)前啟動(dòng)類的包名下,所以理所當(dāng)然能夠被掃描到。
根據(jù)上面的大致分析,可以得到,spring為何優(yōu)先掃描我們程序自定義的類,是因?yàn)樗麜?huì)先默認(rèn)掃描啟動(dòng)類上componentScan注解得包名,如果包名未被設(shè)置,那么默認(rèn)使用啟動(dòng)得包名進(jìn)行掃描,這就是為什么很多博客或者新手使用springboot時(shí)候,大家都說(shuō)把啟動(dòng)類放在最外層得原因所在
至于spring自身定義的bean為何沒有被初始化,是因?yàn)橹饕蛉缦拢?br>


看到?jīng)],先解析啟動(dòng)類componentScan的元數(shù)據(jù),再去解析啟動(dòng)類的上的import的注解元數(shù)據(jù),正是因?yàn)檫@個(gè)先后順序,等去解析自動(dòng)裝配也就是一開始spring內(nèi)置bean的那里的時(shí)候,就會(huì)發(fā)現(xiàn)內(nèi)部已經(jīng)有了當(dāng)前名為restTemplateBuilder的bean,就會(huì)跳過(guò)當(dāng)前內(nèi)置bean的解析流程。
得到了上面的結(jié)果,那么我們可以對(duì)代碼進(jìn)行如下改造:

這時(shí)候就會(huì)發(fā)現(xiàn)spring容器中就只有一個(gè)restTemplateBuilder且是spring自身的。

到此為止,我們可以得出一個(gè)非常有意思的結(jié)論,當(dāng)然也算是spring的規(guī)范,我們?cè)趩?dòng)類上加了componentScan注解后,整個(gè)springboot只會(huì)掃描我們加的這個(gè)注解指定的包名,在也不是默認(rèn)當(dāng)前啟動(dòng)類的默認(rèn)包名,因此在啟動(dòng)類上加這個(gè)注解一定要尤其小心,否則就會(huì)放一些不易發(fā)現(xiàn)的錯(cuò)誤。
至于為什么,或者原理是什么,可以細(xì)看下如下代碼:
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
寫在文后:
關(guān)于下篇文章,我暫時(shí)還沒想好寫哪些,其實(shí)有時(shí)候底層知識(shí)很重要,有時(shí)候框架知識(shí)也很重要,框架一般工作中用的最多,底層考驗(yàn)的是面試和基本功。后續(xù)可能會(huì)陸續(xù)出一些底層相關(guān)的,spring源碼相關(guān)的網(wǎng)上有太多了,我寫的話也只會(huì)寫我比較關(guān)注的,或者大家有什么想了解的我也可以去寫一下。