spring boot容器啓動詳解

目錄

一、前言

spring cloud大行其道的當下,如果不瞭解基本原理那麼是很糾結的(看見的都是約定大於配置,但是原理呢?爲什麼要這麼做?如何串聯起來的?)。spring cloud是基於spring boot快速搭建的,今天咱們就看看spring boot容器啓動流程(全文基於1.5.9版本)。(本文不講解如何快速啓動spring boot,那些直接官方看即可)

 

二、容器啓動

spring boot一般是指定容器啓動main方法,然後以命令行方式啓動Jar包,如下圖:

1 @SpringBootApplication
2 public class Application {
3     public static void main(String[] args) {
4         SpringApplication.run(Application.class, args);
5     }
6 }

這裏核心關注2個東西:

1.@SpringBootApplication註解

2.SpringApplication.run()靜態方法

 下面我們就分別探究這兩塊內容。

2.1 @SpringBootApplication註解

源碼如下:

複製代碼

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 @Inherited
 5 @SpringBootConfiguration
 6 @EnableAutoConfiguration
 7 @ComponentScan(excludeFilters = {
 8         @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
 9         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
10 public @interface SpringBootApplication {

複製代碼

核心註解:

@SpringBootConfiguration(實際就是個@Configuration):表示這是一個JavaConfig配置類,可以在這個類中自定義bean,依賴關係等。-》這個是spring-boot特有的註解,常用到。
@EnableAutoConfiguration:藉助@Import的幫助,將所有符合自動配置條件的bean定義加載到IoC容器(建議放在根包路徑下,這樣可以掃描子包和類)。-》這個需要詳細深挖!
@ComponentScan:spring的自動掃描註解,可定義掃描範圍,加載到IOC容器。-》這個不多說,spring的註解大家肯定眼熟

其中@EnableAutoConfiguration這個註解的源碼:

複製代碼

1 @SuppressWarnings("deprecation")
2 @Target(ElementType.TYPE)
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 @Inherited
6 @AutoConfigurationPackage
7 @Import(EnableAutoConfigurationImportSelector.class)
8 public @interface EnableAutoConfiguration {

複製代碼


 

核心是一個EnableAutoConfigurationImportSelector類圖如下:

核心方法在頂級接口ImportSelector的selectImports(),源碼如下:

複製代碼

 1 @Override
 2     public String[] selectImports(AnnotationMetadata annotationMetadata) {
 3         if (!isEnabled(annotationMetadata)) {
 4             return NO_IMPORTS;
 5         }
 6         try {         //1.從META-INF/spring-autoconfigure-metadata.properties文件中載入483條配置屬性(有一些有默認值),
 7             AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
 8                     .loadMetadata(this.beanClassLoader);
 9             AnnotationAttributes attributes = getAttributes(annotationMetadata);//2.獲取註解屬性
10             List<String> configurations = getCandidateConfigurations(annotationMetadata,//3.獲取97個自動配置類
11                     attributes);
12             configurations = removeDuplicates(configurations);//4.移除重複的
13             configurations = sort(configurations, autoConfigurationMetadata);//5.排序
14             Set<String> exclusions = getExclusions(annotationMetadata, attributes);//6.獲取需要排除的
15             checkExcludedClasses(configurations, exclusions);//7.校驗排除類
16             configurations.removeAll(exclusions);//8.刪除所有需要排除的
17             configurations = filter(configurations, autoConfigurationMetadata);//9.過濾器OnClassCondition(註解中配置的當存在某類才生效)
18             fireAutoConfigurationImportEvents(configurations, exclusions);//10.觸發自動配置導入監聽事件
19             return configurations.toArray(new String[configurations.size()]);
20         }
21         catch (IOException ex) {
22             throw new IllegalStateException(ex);
23         }
24     }

複製代碼

 這裏注意3個核心方法:

1)loadMetadata 加載配置

其實就是用類加載器去加載:META-INF/spring-autoconfigure-metadata.properties(spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar)文件中定義的配置,返回PropertiesAutoConfigurationMetadata(實現了AutoConfigurationMetadata接口,封裝了屬性的get set方法)

2)getCandidateConfigurations獲取默認支持的自動配置類名列表

自動配置靈魂方法,SpringFactoriesLoader.loadFactoryNames 從META-INF/spring.factories(spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar)文件中獲取自動配置類key=EnableAutoConfiguration.class的配置。

複製代碼

 1 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
 2             AnnotationAttributes attributes) {//話說這裏2個入參沒啥用啊...誰來給我解釋一下...
 3         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
 4                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
 5         Assert.notEmpty(configurations,
 6                 "No auto configuration classes found in META-INF/spring.factories. If you "
 7                         + "are using a custom packaging, make sure that file is correct.");
 8         return configurations;
 9     }
10 //返回的是EnableAutoConfiguration類
11 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
12         return EnableAutoConfiguration.class;
13     }

複製代碼

實際獲取了什麼?spring.factories文件如下,實際獲取了# Auto Configure自動配置模塊的所有類。

複製代碼

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure ===========這裏就是全部的自動配置類===============================
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
============================================end================================================
# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider

複製代碼

 3)filter過濾器 根據OnClassCondition註解把不滿足條件的過濾掉

複製代碼

 1 private List<String> filter(List<String> configurations,
 2             AutoConfigurationMetadata autoConfigurationMetadata) {
 3         long startTime = System.nanoTime();
 4         String[] candidates = configurations.toArray(new String[configurations.size()]);
 5         boolean[] skip = new boolean[candidates.length];
 6         boolean skipped = false;
           //獲取需要過濾的自動配置導入攔截器,spring.factories配置中就一個:org.springframework.boot.autoconfigure.condition.OnClassCondition
 7         for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
 8             invokeAwareMethods(filter);
 9             boolean[] match = filter.match(candidates, autoConfigurationMetadata);
10             for (int i = 0; i < match.length; i++) {
11                 if (!match[i]) {
12                     skip[i] = true;
13                     skipped = true;
14                 }
15             }
16         }
17         if (!skipped) {//多條件只要有一個不匹配->skipped = true,全部匹配-》skipped = false->直接返回
18             return configurations;
19         }
20         List<String> result = new ArrayList<String>(candidates.length);
21         for (int i = 0; i < candidates.length; i++) {
22             if (!skip[i]) {//匹配-》不跳過-》添加進result
23                 result.add(candidates[i]);
24             }
25         }
26         if (logger.isTraceEnabled()) {
27             int numberFiltered = configurations.size() - result.size();
28             logger.trace("Filtered " + numberFiltered + " auto configuration class in "
29                     + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
30                     + " ms");
31         }
32         return new ArrayList<String>(result);
33     }

複製代碼

 

2.2 SpringApplication.run()靜態方法

SpringApplication.run

複製代碼

 1 public ConfigurableApplicationContext run(String... args) {
 2         StopWatch stopWatch = new StopWatch();
 3         stopWatch.start();
 4         ConfigurableApplicationContext context = null;
 5         FailureAnalyzers analyzers = null;
 6         configureHeadlessProperty();
 7         SpringApplicationRunListeners listeners = getRunListeners(args);//1.獲取監聽器
 8         listeners.starting();-->啓動!
 9         try {
10             ApplicationArguments applicationArguments = new DefaultApplicationArguments(
11                     args);
12             ConfigurableEnvironment environment = prepareEnvironment(listeners,//2.準備好環境,觸發ApplicationEnvironmentPreparedEvent事件
13                     applicationArguments);
14             Banner printedBanner = printBanner(environment);//打印啓動提示字符,默認spring的字符圖
15             context = createApplicationContext();//實例化一個可配置應用上下文
16             analyzers = new FailureAnalyzers(context);
17             prepareContext(context, environment, listeners, applicationArguments,//3.準備上下文
18                     printedBanner);
19             refreshContext(context);//4.刷新上下文
20             afterRefresh(context, applicationArguments);//5.刷新上下文後
21             listeners.finished(context, null);--關閉!
22             stopWatch.stop();
23             if (this.logStartupInfo) {
24                 new StartupInfoLogger(this.mainApplicationClass)
25                         .logStarted(getApplicationLog(), stopWatch);
26             }
27             return context;
28         }
29         catch (Throwable ex) {
30             handleRunFailure(context, listeners, analyzers, ex);
31             throw new IllegalStateException(ex);
32         }
33     }

複製代碼

如上圖,容器啓動流程可以分爲5個主要步驟:

1.getRunListeners獲取監聽器(SpringApplicationRunListeners )

實際是SpringApplicationRunListener類

複製代碼

 1 private SpringApplicationRunListeners getRunListeners(String[] args) {
 2     Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
 3     return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
 4             SpringApplicationRunListener.class, types, this, args));
 5 }
 6 
 7 private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
 8     return getSpringFactoriesInstances(type, new Class<?>[] {});
 9 }
10 
11 private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
12         Class<?>[] parameterTypes, Object... args) {
13     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
14     // 使用Set確保的字符串的唯一性
15     Set<String> names = new LinkedHashSet<String>(
16             SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 1.載入工廠名稱集合
17     List<T> instances = createSpringFactoriesInstances(type, parameterTypes,// 2.創建工廠實例
18             classLoader, args, names);
19     AnnotationAwareOrderComparator.sort(instances);// 排序
20     return instances;
21 }

複製代碼

載入工廠名稱(loadFactoryNames)

當前類的類加載器從META-INF/spring.factories文件中獲取SpringApplicationRunListener類的配置

複製代碼

 1 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
 2         String factoryClassName = factoryClass.getName();
 3         try {
 4             Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
 5                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
 6             List<String> result = new ArrayList<String>();
 7             while (urls.hasMoreElements()) {
 8                 URL url = urls.nextElement();
 9                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
10                 String factoryClassNames = properties.getProperty(factoryClassName);
11                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
12             }
13             return result;
14         }
15         catch (IOException ex) {
16             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
17                     "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
18         }
19     }

複製代碼

上圖,獲取到工廠類名後,下面來看看META-INF/spring.factories中定義了啥:

複製代碼

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners  這裏呢,看這裏!!!!
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
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.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer

# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

複製代碼

哇,都是些類全名稱,且key都是接口,value都是實現類。我們根據key=“org.springframework.boot.SpringApplicationRunListener”查詢得到實現類value="org.springframework.boot.context.event.EventPublishingRunListener"事件發佈啓動監聽器,一猜也知道肯定要用”反射”根據類名獲取類實例,下面很快得到驗證...

創建spring工廠實例(createSpringFactoriesInstances)

根據第一步得到的Set<String> names(SpringApplicationRunListener的唯一實現類EventPublishingRunListener)生成"事件發佈啓動監聽器"工廠實例

複製代碼

 1 @SuppressWarnings("unchecked")
 2     private <T> List<T> createSpringFactoriesInstances(Class<T> type,
 3             Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
 4             Set<String> names) {
 5         List<T> instances = new ArrayList<T>(names.size());
 6         for (String name : names) {
 7             try {
 8                 Class<?> instanceClass = ClassUtils.forName(name, classLoader);// 利用反射獲取類
 9                 Assert.isAssignable(type, instanceClass);
10                 Constructor<?> constructor = instanceClass
11                         .getDeclaredConstructor(parameterTypes);// 得到構造器
12                 T instance = (T) BeanUtils.instantiateClass(constructor, args);// 根據構造器和參數構造實例
13                 instances.add(instance);
14             }
15             catch (Throwable ex) {
16                 throw new IllegalArgumentException(
17                         "Cannot instantiate " + type + " : " + name, ex);
18             }
19         }
20         return instances;
21     }

複製代碼

2.準備好環境

構造一個ConfigurableEnvironment,這裏不多說。

3.準備上下文

複製代碼

 1 private void prepareContext(ConfigurableApplicationContext context,
 2             ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
 3             ApplicationArguments applicationArguments, Banner printedBanner) {
 4         context.setEnvironment(environment);
 5         postProcessApplicationContext(context);//單例一個BeanNameGenerator,把ResourceLoader設置進應用上下文
 6         applyInitializers(context);//執行初始化器
 7         listeners.contextPrepared(context);// 監聽器執行上下文"已準備好"方法
 8         if (this.logStartupInfo) {
 9             logStartupInfo(context.getParent() == null);
10             logStartupProfileInfo(context);
11         }
12 
13         // 添加spring boot特殊單例bean
14         context.getBeanFactory().registerSingleton("springApplicationArguments",
15                 applicationArguments);
16         if (printedBanner != null) {
17             context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
18         }
19 
20         // 載入資源
21         Set<Object> sources = getSources();
22         Assert.notEmpty(sources, "Sources must not be empty");
23         load(context, sources.toArray(new Object[sources.size()]));
24         listeners.contextLoaded(context);// 監聽器執行"上下文已加載"方法
25     }

複製代碼

4.刷新上下文

複製代碼

 1 private void refreshContext(ConfigurableApplicationContext context) {
 2     refresh(context);//核心類
 3     if (this.registerShutdownHook) {
 4         try {
 5             context.registerShutdownHook();//註冊關閉鉤子,容器關閉時執行
 6         }
 7         catch (AccessControlException ex) {
 8             // Not allowed in some environments.
 9         }
10     }
11 }
12 
13 protected void refresh(ApplicationContext applicationContext) {
14     Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
15     ((AbstractApplicationContext) applicationContext).refresh();
16 }

複製代碼

最終執行的是AbstractApplicationContext抽象類的refresh方法。

複製代碼

 1 public void refresh() throws BeansException, IllegalStateException {
 2         synchronized (this.startupShutdownMonitor) {
 3             //準備刷新的上下文環境,例如對系統屬性或者環境變量進行準備及驗證。
 4             prepareRefresh();
 5 
 6             //啓動子類的refreshBeanFactory方法.解析xml
 7             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 8 
 9             //爲BeanFactory配置容器特性,例如類加載器、事件處理器等.
10             prepareBeanFactory(beanFactory);
11 
12             try {
13                 //設置BeanFactory的後置處理. 空方法,留給子類拓展用。 
14                 postProcessBeanFactory(beanFactory);
15 
16                 //調用BeanFactory的後處理器, 這些後處理器是在Bean定義中向容器註冊的.  
17                 invokeBeanFactoryPostProcessors(beanFactory);
18 
19                 //註冊Bean的後處理器, 在Bean創建過程中調用.  
20                 registerBeanPostProcessors(beanFactory);
21 
22                 //初始化上下文中的消息源,即不同語言的消息體進行國際化處理  
23                 initMessageSource();
24 
25                 //初始化ApplicationEventMulticaster bean,應用事件廣播器
26                 initApplicationEventMulticaster();
27 
28                 //初始化其它特殊的Bean, 空方法,留給子類拓展用。 
29                 onRefresh();
30 
31                 //檢查並向容器註冊監聽器Bean
32                 registerListeners();
33 
34                 //實例化所有剩餘的(non-lazy-init) 單例Bean.
35                 finishBeanFactoryInitialization(beanFactory);
36 
37                 //發佈容器事件, 結束refresh過程. 
38                 finishRefresh();
39             }
40 
41             catch (BeansException ex) {
42                 if (logger.isWarnEnabled()) {
43                     logger.warn("Exception encountered during context initialization - " +
44                             "cancelling refresh attempt: " + ex);
45                 }
46 
47                 //銷燬已經創建的單例Bean, 以避免資源佔用.
48                 destroyBeans();
49 
50                 //取消refresh操作, 重置active標誌. 
51                 cancelRefresh(ex);
52 
53                 // Propagate exception to caller.
54                 throw ex;
55             }
56 
57             finally {
58                 //重置Spring的核心緩存
59                 resetCommonCaches();
60             }
61         }
62     }

複製代碼

5.刷新完上下文後

spring boot提供的2個供用戶自己拓展的接口:ApplicationRunner和CommandLineRunner。可以在容器啓動完畢後(上下文刷新後)執行,做一些類似數據初始化的操作。

複製代碼

 1 private void callRunners(ApplicationContext context, ApplicationArguments args) {
 2         List<Object> runners = new ArrayList<Object>();
 3         runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());//從上下文中獲取ApplicationRunner類型的bean
 4         runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());//從上下文中獲取CommandLineRunner類型的bean
 5         AnnotationAwareOrderComparator.sort(runners);//排序
 6         for (Object runner : new LinkedHashSet<Object>(runners)) {
 7             if (runner instanceof ApplicationRunner) {
 8                 callRunner((ApplicationRunner) runner, args);//執行
 9             }
10             if (runner instanceof CommandLineRunner) {
11                 callRunner((CommandLineRunner) runner, args);
12             }
13         }
14     }

複製代碼

兩個區別在於入參不同,根據實際情況自己選擇。

複製代碼

 1 public interface CommandLineRunner {
 8     void run(String... args) throws Exception;
10 }
11 
12 public interface ApplicationRunner {
19     void run(ApplicationArguments args) throws Exception;
20 
21 }

複製代碼

 CommandLineRunner中執行參數是原始的java啓動類main方法的String[] args字符串數組參數;ApplicationRunner中的參數經過處理提供一些方法例如:

 1 List<String> getOptionValues(String name); 

 根據名稱獲取值list,java 啓動命令中 --foo=bar --foo=baz,則根據foo參數名返回list["bar", "baz"]

回到頂部

三、總結

 

按照前面的分析,Spring-boot容器啓動流程總體可劃分爲2部分:

1)執行註解:掃描指定範圍下的bean、載入自動配置類對應的bean加載到IOC容器。

2)man方法中具體SpringAppliocation.run(),全流程貫穿SpringApplicationEvent,有6個子類:

ApplicationFailedEvent.class

ApplicationPreparedEvent.class

ApplicationReadyEvent.class

ApplicationStartedEvent.class

ApplicationStartingEvent.class

SpringApplicationEvent.class

這裏用到了很經典的spring事件驅動模型,飛機票:Spring事件驅動模型和觀察者模式

類圖如下:

如上圖,就是一個經典spring 事件驅動模型,包含3種角色:事件發佈者、事件、監聽者。對應到spring-boot中就是:

1.EventPublishingRunListener這個類封裝了事件發佈

2.SpringApplicationEvent是spring-boot中定義的事件(上面說的6種事件),繼承自ApplicationEvent(spring中定義的)

3.監聽者 spring-boot並沒有實現針對上述6種事件的監聽者(我沒找到...),這裏用戶可以自己實現監聽者(上述6種事件)來注入spring boot容器啓動流程,觸發相應的事件。

例如:實現ApplicationListener<ApplicationReadyEvent>這個接口,在容器啓動完畢時最後一步listener.finished時,如果啓動沒有異常,就會執行!可以做一些數據初始化之類的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章