前言
SpringBoot是Spring的包裝,通過(guò)自動(dòng)配置使得SpringBoot可以做到開(kāi)箱即用,上手成本非常低,但是學(xué)習(xí)其實(shí)現(xiàn)原理的成本大大增加,需要先了解熟悉Spring原理。如果還不清楚Spring原理的,可以先查看博主之前的文章,本篇主要分析SpringBoot的啟動(dòng)、自動(dòng)配置、Condition、事件驅(qū)動(dòng)原理。
正文
啟動(dòng)原理
SpringBoot啟動(dòng)非常簡(jiǎn)單,因其內(nèi)置了Tomcat,所以只需要通過(guò)下面幾種方式啟動(dòng)即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@SpringBootApplication (scanBasePackages = { "cn.dark" }) public class SpringbootDemo { public static void main(String[] args) { // 第一種 SpringApplication.run(SpringbootDemo . class , args); // 第二種 new SpringApplicationBuilder(SpringbootDemo . class )).run(args); // 第三種 SpringApplication springApplication = new SpringApplication(SpringbootDemo. class ); springApplication.run(); } } |
可以看到第一種是最簡(jiǎn)單的,也是最常用的方式,需要注意類上面需要標(biāo)注@SpringBootApplication注解,這是自動(dòng)配置的核心實(shí)現(xiàn),稍后分析,先來(lái)看看SpringBoot啟動(dòng)做了些什么?
在往下之前,不妨先猜測(cè)一下,run方法中需要做什么?對(duì)比Spring源碼,我們知道,Spring的啟動(dòng)都會(huì)創(chuàng)建一個(gè)ApplicationContext的應(yīng)用上下文對(duì)象,并調(diào)用其refresh方法啟動(dòng)容器,SpringBoot只是Spring的一層殼,肯定也避免不了這樣的操作。
另一方面,以前通過(guò)Spring搭建的項(xiàng)目,都需要打成War包發(fā)布到Tomcat才行,而現(xiàn)在SpringBoot已經(jīng)內(nèi)置了Tomcat,只需要打成Jar包啟動(dòng)即可,所以在run方法中肯定也會(huì)創(chuàng)建對(duì)應(yīng)的Tomcat對(duì)象并啟動(dòng)。
以上只是我們的猜想,下面就來(lái)驗(yàn)證,進(jìn)入run方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
public ConfigurableApplicationContext run(String... args) { // 統(tǒng)計(jì)時(shí)間用的工具類 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null ; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 獲取實(shí)現(xiàn)了SpringApplicationRunListener接口的實(shí)現(xiàn)類,通過(guò)SPI機(jī)制加載 // META-INF/spring.factories文件下的類 SpringApplicationRunListeners listeners = getRunListeners(args); // 首先調(diào)用SpringApplicationRunListener的starting方法 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 處理配置數(shù)據(jù) ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 啟動(dòng)時(shí)打印banner Banner printedBanner = printBanner(environment); // 創(chuàng)建上下文對(duì)象 context = createApplicationContext(); // 獲取SpringBootExceptionReporter接口的類,異常報(bào)告 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter. class , new Class[] { ConfigurableApplicationContext. class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 核心方法,啟動(dòng)spring容器 refreshContext(context); afterRefresh(context, applicationArguments); // 統(tǒng)計(jì)結(jié)束 stopWatch.stop(); if ( this .logStartupInfo) { new StartupInfoLogger( this .mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 調(diào)用started listeners.started(context); // ApplicationRunner // CommandLineRunner // 獲取這兩個(gè)接口的實(shí)現(xiàn)類,并調(diào)用其run方法 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 最后調(diào)用running方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null ); throw new IllegalStateException(ex); } return context; } |
SpringBoot的啟動(dòng)流程就是這個(gè)方法,先看getRunListeners方法,這個(gè)方法就是去拿到所有的SpringApplicationRunListener實(shí)現(xiàn)類,這些類是用于SpringBoot事件發(fā)布的,關(guān)于事件驅(qū)動(dòng)稍后分析,這里主要看這個(gè)方法的實(shí)現(xiàn)原理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication. class , String[]. class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener. class , types, this , args)); } 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)); // 加載上來(lái)后反射實(shí)例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ; 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; } } |
一步步追蹤下去可以看到最終就是通過(guò)SPI機(jī)制根據(jù)接口類型從META-INF/spring.factories文件中加載對(duì)應(yīng)的實(shí)現(xiàn)類并實(shí)例化,SpringBoot的自動(dòng)配置也是這樣實(shí)現(xiàn)的。
為什么要這樣實(shí)現(xiàn)呢?通過(guò)注解掃描不可以么?當(dāng)然不行,這些類都在第三方j(luò)ar包中,注解掃描實(shí)現(xiàn)是很麻煩的,當(dāng)然你也可以通過(guò)@Import注解導(dǎo)入,但是這種方式不適合擴(kuò)展類特別多的情況,所以這里采用SPI的優(yōu)點(diǎn)就顯而易見(jiàn)了。
回到run方法中,可以看到調(diào)用了createApplicationContext方法,見(jiàn)名知意,這個(gè)就是去創(chuàng)建應(yīng)用上下文對(duì)象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext" ; protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this .applicationContextClass; if (contextClass == null ) { try { switch ( this .webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break ; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break ; default : contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass" , ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } |
注意這里通過(guò)反射實(shí)例化了一個(gè)新的沒(méi)見(jiàn)過(guò)的上下文對(duì)象AnnotationConfigServletWebServerApplicationContext,這個(gè)是SpringBoot擴(kuò)展的,看看其構(gòu)造方法:
1
2
3
4
|
public AnnotationConfigServletWebServerApplicationContext() { this .reader = new AnnotatedBeanDefinitionReader( this ); this .scanner = new ClassPathBeanDefinitionScanner( this ); } |
如果你有看過(guò)Spring注解驅(qū)動(dòng)的實(shí)現(xiàn)原理,這兩個(gè)對(duì)象肯定不會(huì)陌生,一個(gè)實(shí)支持注解解析的,另外一個(gè)是掃描包用的。上下文創(chuàng)建好了,下一步自然就是調(diào)用refresh方法啟動(dòng)容器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if ( this .registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext. class , applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); } |
這里首先會(huì)調(diào)用到其父類中ServletWebServerApplicationContext:
1
2
3
4
5
6
7
8
9
|
public final void refresh() throws BeansException, IllegalStateException { try { super .refresh(); } catch (RuntimeException ex) { stopAndReleaseWebServer(); throw ex; } } |
可以看到是直接委托給了父類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
public void refresh() throws BeansException, IllegalStateException { synchronized ( this .startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. 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); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } |
這個(gè)方法不會(huì)陌生吧,之前已經(jīng)分析過(guò)了,這里不再贅述,至此SpringBoot的容器就啟動(dòng)了,但是Tomcat啟動(dòng)是在哪里呢?run方法中也沒(méi)有看到。實(shí)際上Tomcat的啟動(dòng)也是在refresh流程中,這個(gè)方法其中一步是調(diào)用了onRefresh方法,在Spring中這是一個(gè)沒(méi)有實(shí)現(xiàn)的模板方法,而SpringBoot就通過(guò)這個(gè)方法完成了Tomcat的啟動(dòng):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
protected void onRefresh() { super .onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException( "Unable to start web server" , ex); } } private void createWebServer() { WebServer webServer = this .webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null ) { ServletWebServerFactory factory = getWebServerFactory(); // 主要看這個(gè)方法 this .webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null ) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException( "Cannot initialize servlet context" , ex); } } initPropertySources(); } |
這里首先拿到TomcatServletWebServerFactory對(duì)象,通過(guò)該對(duì)象再去創(chuàng)建和啟動(dòng)Tomcat:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public WebServer getWebServer(ServletContextInitializer... initializers) { if ( this .disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat(); File baseDir = ( this .baseDirectory != null ) ? this .baseDirectory : createTempDir( "tomcat" ); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector( this .protocol); connector.setThrowOnFailure( true ); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy( false ); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this .additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); } |
上面的每一步都可以對(duì)比Tomcat的配置文件,需要注意默認(rèn)只支持了http協(xié)議:
1
2
3
4
|
Connector connector = new Connector( this .protocol); private String protocol = DEFAULT_PROTOCOL; public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol" ; |
如果想要擴(kuò)展的話則可以對(duì)additionalTomcatConnectors屬性設(shè)置值,需要注意這個(gè)屬性沒(méi)有對(duì)應(yīng)的setter方法,只有addAdditionalTomcatConnectors方法,也就是說(shuō)我們只能通過(guò)實(shí)現(xiàn)BeanFactoryPostProcessor接口的postProcessBeanFactory方法,而不能通過(guò)BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,因?yàn)榍罢呖梢酝ㄟ^(guò)傳入的BeanFactory對(duì)象提前獲取到TomcatServletWebServerFactory對(duì)象調(diào)用addAdditionalTomcatConnectors即可;而后者只能拿到BeanDefinition對(duì)象,該對(duì)象只能通過(guò)setter方法設(shè)置值。
事件驅(qū)動(dòng)
Spring原本就提供了事件機(jī)制,而在SpringBoot中又對(duì)其進(jìn)行擴(kuò)展,通過(guò)發(fā)布訂閱事件在容器的整個(gè)生命周期的不同階段進(jìn)行不同的操作。我們先來(lái)看看SpringBoot啟動(dòng)關(guān)閉的過(guò)程中默認(rèn)會(huì)發(fā)布哪些事件,使用下面的代碼即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@SpringBootApplication public class SpringEventDemo { public static void main(String[] args) { new SpringApplicationBuilder(SpringEventDemo. class ) .listeners(event -> { System.err.println( "接收到事件:" + event.getClass().getSimpleName()); }) .run() .close(); } } |
這段代碼會(huì)在控制臺(tái)打印所有的事件名稱,按照順序如下:
ApplicationStartingEvent:容器啟動(dòng)
ApplicationEnvironmentPreparedEvent:環(huán)境準(zhǔn)備好
ApplicationContextInitializedEvent:上下文初始化完成
ApplicationPreparedEvent:上下文準(zhǔn)備好
ContextRefreshedEvent:上下文刷新完
ServletWebServerInitializedEvent:webServer初始化完成
ApplicationStartedEvent:容器啟動(dòng)完成
ApplicationReadyEvent:容器就緒
ContextClosedEvent:容器關(guān)閉
以上是正常啟動(dòng)關(guān)閉,如果發(fā)生異常還有發(fā)布ApplicationFailedEvent事件。事件的發(fā)布遍布在整個(gè)容器的啟動(dòng)關(guān)閉周期中,事件發(fā)布對(duì)象剛剛我們也看到了是通過(guò)SPI加載的SpringApplicationRunListener實(shí)現(xiàn)類EventPublishingRunListener,同樣事件監(jiān)聽(tīng)器也是在spring.factories文件中配置的,默認(rèn)實(shí)現(xiàn)了以下監(jiān)聽(tīng)器:
1
2
3
4
5
6
7
8
9
10
11
|
org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener |
可以看到有用于文件編碼的(FileEncodingApplicationListener),有加載日志框架的(LoggingApplicationListener),還有加載配置的(ConfigFileApplicationListener)等等一系列監(jiān)聽(tīng)器,SpringBoot也就是通過(guò)這系列監(jiān)聽(tīng)器將必要的配置和組件加載到容器中來(lái),這里不再詳細(xì)分析,感興趣的讀者可以通過(guò)其實(shí)現(xiàn)的onApplicationEvent方法看到每個(gè)監(jiān)聽(tīng)器究竟是監(jiān)聽(tīng)的哪一個(gè)事件,當(dāng)然事件發(fā)布和監(jiān)聽(tīng)我們自己也是可以擴(kuò)展的。
自動(dòng)配置原理
SpringBoot最核心的還是自動(dòng)配置,為什么它能做到開(kāi)箱即用,不再需要我們手動(dòng)使用@EnableXXX等注解來(lái)開(kāi)啟?這一切的答案就在@SpringBootApplication注解中:
1
2
3
4
5
6
7
8
9
|
@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 ) }) public @interface SpringBootApplication {} |
這里重要的注解有三個(gè):@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。@ComponentScan就不用再說(shuō)了,@SpringBootConfiguration等同于@Configuration,而@EnableAutoConfiguration就是開(kāi)啟自動(dòng)配置:
1
2
3
4
5
6
7
8
9
10
|
@AutoConfigurationPackage @Import (AutoConfigurationImportSelector. class ) public @interface EnableAutoConfiguration { } @Import (AutoConfigurationPackages.Registrar. class ) public @interface AutoConfigurationPackage { } |
@AutoConfigurationPackage注解的作用就是將該注解所標(biāo)記類所在的包作為自動(dòng)配置的包,簡(jiǎn)單看看就行,主要看AutoConfigurationImportSelector,這個(gè)就是實(shí)現(xiàn)自動(dòng)配置的核心類,注意這個(gè)類是實(shí)現(xiàn)的DeferredImportSelector接口。在這個(gè)類中有一個(gè)selectImports方法。
這個(gè)方法在我之前的文章這一次搞懂Spring事務(wù)注解的解析也有分析過(guò),只是實(shí)現(xiàn)類不同,它同樣會(huì)被ConfigurationClassPostProcessor類調(diào)用,先來(lái)看這個(gè)方法做了些什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata( this .beanClassLoader); // 獲取所有的自動(dòng)配置類 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); // SPI獲取EnableAutoConfiguration為key的所有實(shí)現(xiàn)類 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 把某些自動(dòng)配置類過(guò)濾掉 configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); // 包裝成自動(dòng)配置實(shí)體類 return new AutoConfigurationEntry(configurations, exclusions); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // SPI獲取EnableAutoConfiguration為key的所有實(shí)現(xiàn)類 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; } |
追蹤源碼最終可以看到也是從META-INF/spring.factories文件中拿到所有EnableAutoConfiguration對(duì)應(yīng)的值(在spring-boot-autoconfigure中)并通過(guò)反射實(shí)例化,過(guò)濾后包裝成AutoConfigurationEntry對(duì)象返回。
看到這里你應(yīng)該會(huì)覺(jué)得自動(dòng)配置的實(shí)現(xiàn)就是通過(guò)這個(gè)selectImports方法,但實(shí)際上這個(gè)方法通常并不會(huì)被調(diào)用到,而是會(huì)調(diào)用該類的內(nèi)部類AutoConfigurationGroup的process和selectImports方法,前者同樣是通過(guò)getAutoConfigurationEntry拿到所有的自動(dòng)配置類,而后者這是過(guò)濾排序并包裝后返回。
下面就來(lái)分析ConfigurationClassPostProcessor是怎么調(diào)用到這里的,直接進(jìn)入processConfigBeanDefinitions方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null ) { if (logger.isDebugEnabled()) { logger.debug( "Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this .metadataReaderFactory)) { configCandidates.add( new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return ; } // Sort by previously determined @Order value, if applicable 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 ; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (! this .localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.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 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()); } this .reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); // 省略。。。。 } |
前面一大段主要是拿到合格的Configuration配置類,主要邏輯是在ConfigurationClassParser.parse方法中,該方法完成了對(duì)@Component、@Bean、@Import、@ComponentScans等注解的解析,這里主要看對(duì)@Import的解析,其它的讀者可自行分析。
一步步追蹤,最終會(huì)進(jìn)入到processConfigurationClass方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if ( this .conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return ; } 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 ); this .configurationClasses.put(configClass, configClass); } |
這里需要注意this.conditionEvaluator.shouldSkip方法的調(diào)用,這個(gè)方法就是進(jìn)行Bean加載過(guò)濾的,即根據(jù)@Condition注解的匹配值判斷是否加載該Bean,具體實(shí)現(xiàn)稍后分析,繼續(xù)跟蹤主流程doProcessConfigurationClass:
1
2
3
4
5
6
7
8
9
10
|
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { 省略.... // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true ); 省略.... return null ; } |
這里就是完成對(duì)一系列注解的支撐,我省略掉了,主要看processImports方法,這個(gè)方法就是處理@Import注解的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return ; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this .problemReporter.error( new CircularImportProblem(configClass, this .importStack)); } else { this .importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector. class )) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector. class , this .environment, this .resourceLoader, this .registry); if (selector instanceof DeferredImportSelector) { this .deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false ); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar. class )) { Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar. class , this .environment, this .resourceLoader, this .registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { this .importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } } } |
剛剛我提醒過(guò)AutoConfigurationImportSelector是實(shí)現(xiàn)DeferredImportSelector接口的,如果不是該接口的實(shí)現(xiàn)類則是直接調(diào)用selectImports方法,反之則是調(diào)用DeferredImportSelectorHandler.handle方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>(); public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder( configClass, importSelector); if ( this .deferredImportSelectors == null ) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); handler.processGroupImports(); } else { this .deferredImportSelectors.add(holder); } } |
首先創(chuàng)建了一個(gè)DeferredImportSelectorHolder對(duì)象,如果是第一次執(zhí)行則是添加到deferredImportSelectors屬性中,等到ConfigurationClassParser.parse的最后調(diào)用process方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public void parse(Set<BeanDefinitionHolder> configCandidates) { 省略..... this .deferredImportSelectorHandler.process(); } public void process() { List<DeferredImportSelectorHolder> deferredImports = this .deferredImportSelectors; this .deferredImportSelectors = null ; try { if (deferredImports != null ) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); handler.processGroupImports(); } } finally { this .deferredImportSelectors = new ArrayList<>(); } } |
反之則是直接執(zhí)行,首先通過(guò)register拿到AutoConfigurationGroup對(duì)象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public void register(DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector() .getImportGroup(); DeferredImportSelectorGrouping grouping = this .groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); this .configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } public Class<? extends Group> getImportGroup() { return AutoConfigurationGroup. class ; } |
然后在processGroupImports方法中進(jìn)行真正的處理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this .groupings.values()) { grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this .configurationClasses.get( entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false ); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]" , ex); } }); } } public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this .deferredImports) { this .group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this .group.selectImports(); } |
在getImports方法中就完成了對(duì)process和selectImports方法的調(diào)用,拿到自動(dòng)配置類后再遞歸調(diào)用調(diào)用processImports方法完成對(duì)自動(dòng)配置類的加載。
至此,自動(dòng)配置的加載過(guò)程就分析完了,下面是時(shí)序圖:
Condition注解原理
在自動(dòng)配置類中有很多Condition相關(guān)的注解,以AOP為例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
Configuration(proxyBeanMethods = false ) @ConditionalOnProperty (prefix = "spring.aop" , name = "auto" , havingValue = "true" , matchIfMissing = true ) public class AopAutoConfiguration { @Configuration (proxyBeanMethods = false ) @ConditionalOnClass (Advice. class ) static class AspectJAutoProxyingConfiguration { @Configuration (proxyBeanMethods = false ) @EnableAspectJAutoProxy (proxyTargetClass = false ) @ConditionalOnProperty (prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "false" , matchIfMissing = false ) static class JdkDynamicAutoProxyConfiguration { } @Configuration (proxyBeanMethods = false ) @EnableAspectJAutoProxy (proxyTargetClass = true ) @ConditionalOnProperty (prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" , matchIfMissing = true ) static class CglibAutoProxyConfiguration { } } @Configuration (proxyBeanMethods = false ) @ConditionalOnMissingClass ( "org.aspectj.weaver.Advice" ) @ConditionalOnProperty (prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" , matchIfMissing = true ) static class ClassProxyingConfiguration { ClassProxyingConfiguration(BeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } } } } |
這里就能看到@ConditionalOnProperty、@ConditionalOnClass、@ConditionalOnMissingClass,另外還有@ConditionalOnBean、@ConditionalOnMissingBean等等很多條件匹配注解。這些注解表示條件匹配才會(huì)加載該Bean,以@ConditionalOnProperty為例,表明配置文件中符合條件才會(huì)加載對(duì)應(yīng)的Bean,prefix表示在配置文件中的前綴,name表示配置的名稱,havingValue表示配置為該值時(shí)才匹配,matchIfMissing則是表示沒(méi)有該配置是否默認(rèn)加載對(duì)應(yīng)的Bean。
其它注解可類比理解記憶,下面主要來(lái)分析該注解的實(shí)現(xiàn)原理。
這里注解點(diǎn)進(jìn)去看會(huì)發(fā)現(xiàn)每個(gè)注解上都標(biāo)注了@Conditional注解,并且value值都對(duì)應(yīng)一個(gè)類,比如OnBeanCondition,而這些類都實(shí)現(xiàn)了Condition接口,看看其繼承體系:
上面只展示了幾個(gè)實(shí)現(xiàn)類,但實(shí)際上Condition的實(shí)現(xiàn)類是非常多的,我們還可以自己實(shí)現(xiàn)該接口來(lái)擴(kuò)展@Condition注解。
Condition接口中有一個(gè)matches方法,這個(gè)方法返回true則表示匹配。該方法在ConfigurationClassParser中多處都有調(diào)用,也就是剛剛我提醒過(guò)的shouldSkip方法,具體實(shí)現(xiàn)是在ConditionEvaluator類中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public boolean shouldSkip( @Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional. class .getName())) { return false ; } if (phase == null ) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this .context.getClassLoader()); conditions.add(condition); } } AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null ; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if ((requiredPhase == null || requiredPhase == phase) && !condition.matches( this .context, metadata)) { return true ; } } return false ; } |
再來(lái)看看matches的實(shí)現(xiàn),但OnBeanCondition類中沒(méi)有實(shí)現(xiàn)該方法,而是在其父類SpringBootCondition中:
1
2
3
4
5
6
7
8
|
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String classOrMethodName = getClassOrMethodName(metadata); try { ConditionOutcome outcome = getMatchOutcome(context, metadata); logOutcome(classOrMethodName, outcome); recordEvaluation(context, classOrMethodName, outcome); return outcome.isMatch(); } |
getMatchOutcome方法也是一個(gè)模板方法,具體的匹配邏輯就在這個(gè)方法中實(shí)現(xiàn),該方法返回的ConditionOutcome對(duì)象就包含了是否匹配和日志消息兩個(gè)字段。進(jìn)入到OnBeanCondition類中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage matchMessage = ConditionMessage.empty(); MergedAnnotations annotations = metadata.getAnnotations(); if (annotations.isPresent(ConditionalOnBean. class )) { Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean. class ); MatchResult matchResult = getMatchingBeans(context, spec); if (!matchResult.isAllMatched()) { String reason = createOnBeanNoMatchReason(matchResult); return ConditionOutcome.noMatch(spec.message().because(reason)); } matchMessage = spec.message(matchMessage).found( "bean" , "beans" ).items(Style.QUOTE, matchResult.getNamesOfAllMatches()); } if (metadata.isAnnotated(ConditionalOnSingleCandidate. class .getName())) { Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations); MatchResult matchResult = getMatchingBeans(context, spec); if (!matchResult.isAllMatched()) { return ConditionOutcome.noMatch(spec.message().didNotFind( "any beans" ).atAll()); } else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(), spec.getStrategy() == SearchStrategy.ALL)) { return ConditionOutcome.noMatch(spec.message().didNotFind( "a primary bean from beans" ) .items(Style.QUOTE, matchResult.getNamesOfAllMatches())); } matchMessage = spec.message(matchMessage).found( "a primary bean from beans" ).items(Style.QUOTE, matchResult.getNamesOfAllMatches()); } if (metadata.isAnnotated(ConditionalOnMissingBean. class .getName())) { Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnMissingBean. class ); MatchResult matchResult = getMatchingBeans(context, spec); if (matchResult.isAnyMatched()) { String reason = createOnMissingBeanNoMatchReason(matchResult); return ConditionOutcome.noMatch(spec.message().because(reason)); } matchMessage = spec.message(matchMessage).didNotFind( "any beans" ).atAll(); } return ConditionOutcome.match(matchMessage); } |
可以看到該類支持了@ConditionalOnBean、@ConditionalOnSingleCandidate、@ConditionalOnMissingBean注解,主要的匹配邏輯在getMatchingBeans方法中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) { ClassLoader classLoader = context.getClassLoader(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT; Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers(); if (spec.getStrategy() == SearchStrategy.ANCESTORS) { BeanFactory parent = beanFactory.getParentBeanFactory(); Assert.isInstanceOf(ConfigurableListableBeanFactory. class , parent, "Unable to use SearchStrategy.ANCESTORS" ); beanFactory = (ConfigurableListableBeanFactory) parent; } MatchResult result = new MatchResult(); Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers); for (String type : spec.getTypes()) { Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type, parameterizedContainers); typeMatches.removeAll(beansIgnoredByType); if (typeMatches.isEmpty()) { result.recordUnmatchedType(type); } else { result.recordMatchedType(type, typeMatches); } } for (String annotation : spec.getAnnotations()) { Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation, considerHierarchy); annotationMatches.removeAll(beansIgnoredByType); if (annotationMatches.isEmpty()) { result.recordUnmatchedAnnotation(annotation); } else { result.recordMatchedAnnotation(annotation, annotationMatches); } } for (String beanName : spec.getNames()) { if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) { result.recordMatchedName(beanName); } else { result.recordUnmatchedName(beanName); } } return result; } |
這里邏輯看起來(lái)比較復(fù)雜,但實(shí)際上就做了兩件事,首先通過(guò)getNamesOfBeansIgnoredByType方法調(diào)用beanFactory.getBeanNamesForType拿到容器中對(duì)應(yīng)的Bean實(shí)例,然后根據(jù)返回的結(jié)果判斷哪些Bean存在,哪些Bean不存在(Condition注解中是可以配置多個(gè)值的)并返回MatchResult對(duì)象,而MatchResult中只要有一個(gè)Bean沒(méi)有匹配上就返回false,也就決定了當(dāng)前Bean是否需要實(shí)例化。
總結(jié)
本篇分析了SpringBoot核心原理的實(shí)現(xiàn),通過(guò)本篇相信讀者也將能更加熟練地使用和擴(kuò)展SpringBoot。另外還有一些常用的組件我沒(méi)有展開(kāi)分析,如事務(wù)、MVC、監(jiān)聽(tīng)器的自動(dòng)配置,這些我們有了Spring源碼基礎(chǔ)的話下來(lái)看一下就明白了,這里就不贅述了。最后讀者可以思考一下我們應(yīng)該如何自定義starter啟動(dòng)器,相信看完本篇應(yīng)該難不倒你。
以上這篇基于SpringBoot核心原理(自動(dòng)配置、事件驅(qū)動(dòng)、Condition)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/l6108003/article/details/106966386