SpringBoot(1.5.6.RELEASE)源碼解析

轉自 https://www.cnblogs.com/dylan-java/p/7450914.htmlhtml

啓動SpringBoot,須要在入口函數所在的類上添加@SpringBootApplication註解java

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

咱們來看一下@SpringBootApplication註解react

 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 {
11     @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
12     Class<?>[] exclude() default {};
13 
14     @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
15     String[] excludeName() default {};
16 
17     @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
18     String[] scanBasePackages() default {};
19 
20     @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
21     Class<?>[] scanBasePackageClasses() default {};
22 }
1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 @Configuration
5 public @interface SpringBootConfiguration {
6 }

從上面的代碼能夠看出@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScanweb

@Configuration註解(能夠理解爲xml裏面的<beans>標籤),通常和@Bean註解(能夠理解爲xml裏面的<bean>標籤)搭配使用。使用這2個註解能夠建立一個配置類,示例以下redis

 1 @Configuration
 2 public class Config {
 3     @Bean
 4     public People people() {
 5         People people = new People();
 6         people.setAge(26);
 7         people.setName("Dylan");
 8         people.setGender(1);
 9         return people;
10     }
11 }
複製代碼

能夠替代xml配置文件裏spring

複製代碼
1 <beans>
2     <bean id="people" class="com.dylan.java.beans.People">
3         <property name="age" value="26"></property>
4         <property name="name" value="Dylan"></property>
5         <property name="gender" value="1"></property>
6     </bean>
7 </beans>

@EnableAutoConfiguration註解自動載入應用程序所須要的全部Bean,這依賴於SpringBoot在類路徑中的查找數組

 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 {
 9     String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
10 
11     Class<?>[] exclude() default {};
12 
13     String[] excludeName() default {};
14 }
複製代碼

@EnableAutoConfiguration註解使用@Import註解導入EnableAutoConfigurationImportSelector類,此類繼承了AutoConfigurationImportSelector類,而AutoConfigurationImportSelector類的selectImports方法就是關鍵所在緩存

複製代碼
 1 @Override
 2 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 3     if (!isEnabled(annotationMetadata)) {
 4         return NO_IMPORTS;
 5     }
 6     try {
 7         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
 8                 .loadMetadata(this.beanClassLoader);
 9         AnnotationAttributes attributes = getAttributes(annotationMetadata);
10         List<String> configurations = getCandidateConfigurations(annotationMetadata,
11                 attributes);
12         configurations = removeDuplicates(configurations);
13         configurations = sort(configurations, autoConfigurationMetadata);
14         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
15         checkExcludedClasses(configurations, exclusions);
16         configurations.removeAll(exclusions);
17         configurations = filter(configurations, autoConfigurationMetadata);
18         fireAutoConfigurationImportEvents(configurations, exclusions);
19         return configurations.toArray(new String[configurations.size()]);
20     }
21     catch (IOException ex) {
22         throw new IllegalStateException(ex);
23     }
24 }
1 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
2             AnnotationAttributes attributes) {
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 }
複製代碼
1 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
2     return EnableAutoConfiguration.class;
3 }

能夠看出,該方法使用了Spring Core包的SpringFactoriesLoader類的loadFactoryNames方法,該方法會查詢classpath下的JAR文件中包含的/META/spring.factories文件,從文件中讀取配置文件名(這裏是org.springframework.boot.autoconfigure.EnableAutoConfiguration)的屬性,例如tomcat

在jar:file:/C:/Users/guiqingqing/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/1.5.6.RELEASE/spring-boot-autoconfigure-1.5.6.RELEASE.jar!/META-INF/spring.factories文件找到以下配置安全

# 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

獲取自動配置的類以後調用removeDuplicates方法先去除重複,而後調用sort方法進行排序,接下來調用getExclusions方法獲取配置的exclude的類,好比@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}),而後這些exclude的類也會被排除

而後是調用filter方法,該方法決定哪些bean自動配置,哪些不配置。去classpath下的JAR文件中包含的/META/spring.factories文件裏查詢org.springframework.boot.autoconfigure.AutoConfigurationImportFilter對應的配置,這裏找到的是org.springframework.boot.autoconfigure.condition.OnClassCondition,檢查規則以下

通過上面的去重,排序,排除,(這裏會有2個線程分別對前一半和後一半作檢查)遍歷全部剩下的class

首先看是否有@ConditionalOnClass註解,若是沒有那麼不過濾,表示須要配置這個bean

若是有@ConditionalOnClass註解再看這個註解依賴的類是否爲空,若是爲空,那麼也不過濾,表示須要配置這個bean

若是@ConditionalOnClass註解依賴的類不爲空,那麼再看classpath下能不能找到這個依賴的類,若是能找不到,那麼也不過濾,表示須要配置這個bean,不然過濾,表示不配置這個bean

例如FreeMarkerAutoConfiguration類

1 @Configuration
2 @ConditionalOnClass({ freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class })
4 @AutoConfigureAfter(WebMvcAutoConfiguration.class)
5 @EnableConfigurationProperties(FreeMarkerProperties.class)
6 public class FreeMarkerAutoConfiguration {
7 }

該類的@ConditionalOnClass註解裏有freemarker.template.Configuration.class和FreeMarkerConfigurationFactory.class類,若是classpath下能找到這2個類,那麼就會自動加載FreeMarkerAutoConfiguration類

最後去classpath下的JAR文件中包含的/META/spring.factories文件裏查詢org.springframework.boot.autoconfigure.AutoConfigurationImportListener對應的屬性做爲監聽器,而後發佈一個AutoConfigurationImportEvent事件

最後交代一下,selectImports方法的調用時機,是在SpringBoot啓動時,AbstractApplicationContext類refresh方法 -> invokeBeanFactoryPostProcessors方法 -> PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors方法 -> invokeBeanDefinitionRegistryPostProcessors方法 -> ConfigurationClassPostProcessor類的postProcessBeanDefinitionRegistry方法 -> processConfigBeanDefinitions方法 -> ConfigurationClassParser類的parse方法 -> processDeferredImportSelectors方法

 1 private void processDeferredImportSelectors() {
 2     List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
 3     this.deferredImportSelectors = null;
 4     Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
 5 
 6     for (DeferredImportSelectorHolder deferredImport : deferredImports) {
 7         ConfigurationClass configClass = deferredImport.getConfigurationClass();
 8         try {
 9             String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
10             processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
11         }
12         catch (BeanDefinitionStoreException ex) {
13             throw ex;
14         }
15         catch (Throwable ex) {
16             throw new BeanDefinitionStoreException(
17                     "Failed to process import candidates for configuration class [" +
18                     configClass.getMetadata().getClassName() + "]", ex);
19         }
20     }
21 }

@ComponentScan註解會自動掃描指定包(若是不指定,那麼默認掃描當前類路徑以及子路徑下的全部類)下的所有標有@Component(包括@Service、@Repository、@Controller等子註解)的類,並註冊成bean

 接下來從SpringApplication.run(Application.class, args);代碼開始一行行DEBUG進行分析

1 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
2 public class Application {
3     public static void main(String[] args) {
4         SpringApplication.run(Application.class, args);
5     }
6 }
1 public static ConfigurableApplicationContext run(Object source, String... args) {
2     return run(new Object[] { source }, args);
3 }
1 public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
2     return new SpringApplication(sources).run(args);
3 }

最終會建立一個SpringApplication的對象,而後調用它的run方法

1 public SpringApplication(Object... sources) {
2     initialize(sources);
3 }

SpringApplication的構造函數會調用initialize方法進行初始化

複製代碼
 1 @SuppressWarnings({ "unchecked", "rawtypes" })
 2 private void initialize(Object[] sources) {
 3     if (sources != null && sources.length > 0) {
 4         this.sources.addAll(Arrays.asList(sources));
 5     }
 6     this.webEnvironment = deduceWebEnvironment();
 7     setInitializers((Collection) getSpringFactoriesInstances(
 8             ApplicationContextInitializer.class));
 9     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
10     this.mainApplicationClass = deduceMainApplicationClass();
11 }
複製代碼

首先,會把sources參數(也就是com.dylan.java.springboot.template.Application的class對象)添加到SpringApplication對象的sources屬性,該屬性是一個LinkedHashSet類型

而後接下來的一行調用了deduceWebEnvironment方法

複製代碼
1 private boolean deduceWebEnvironment() {
2     for (String className : WEB_ENVIRONMENT_CLASSES) {
3         if (!ClassUtils.isPresent(className, null)) {
4             return false;
5         }
6     }
7     return true;
8 }
複製代碼
1 private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
2             "org.springframework.web.context.ConfigurableWebApplicationContext" };

該方法會調用ClassUtils類的isPresent方法,檢查classpath中是否存在javax.servlet.Servlet類和org.springframework.web.context.ConfigurableWebApplicationContext類,並賦值給SpringApplication對象的webEnvironment屬性,該屬性是一個boolean類型,表示啓動的程序是不是一個web應用程序

接下來是第7行,主要是初始化ApplicationContextInitializer

在classpath下的JAR文件中包含的/META/spring.factories文件裏找到org.springframework.context.ApplicationContextInitializer對應的屬性,而後實例化並排序,設置到SpringApplication對象的initializers屬性,該屬性是一個ArrayList類型

接下來是第8行,主要是初始化ApplicationListener

在classpath下的JAR文件中包含的/META/spring.factories文件裏找到org.springframework.context.ApplicationListener對應的屬性,而後實例化並排序,設置到SpringApplication對象的listeners屬性,該屬性是一個ArrayList類型

最後調用deduceMainApplicationClass方法找到main函數所在的類,實現的方式是獲取當前方法調用棧,找到main函數的類,並設置到SpringApplication對象的mainApplicationClass屬性,該屬性是一個Class類型

複製代碼
 1 private Class<?> deduceMainApplicationClass() {
 2     try {
 3         StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
 4         for (StackTraceElement stackTraceElement : stackTrace) {
 5             if ("main".equals(stackTraceElement.getMethodName())) {
 6                 return Class.forName(stackTraceElement.getClassName());
 7             }
 8         }
 9     }
10     catch (ClassNotFoundException ex) {
11         // Swallow and continue
12     }
13     return null;
14 }
複製代碼

至此,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);
 8     listeners.starting();
 9     try {
10         ApplicationArguments applicationArguments = new DefaultApplicationArguments(
11                 args);
12         ConfigurableEnvironment environment = prepareEnvironment(listeners,
13                 applicationArguments);
14         Banner printedBanner = printBanner(environment);
15         context = createApplicationContext();
16         analyzers = new FailureAnalyzers(context);
17         prepareContext(context, environment, listeners, applicationArguments,
18                 printedBanner);
19         refreshContext(context);
20         afterRefresh(context, applicationArguments);
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 }
複製代碼

首先建立一個StopWatch對象並調用它的start方法,該類是Spring提供的一個計時器類,與本篇要討論的東西無關,所在在這裏不對它進行分析

第6行調用了configureHeadlessProperty方法,該方法只有一行代碼,就是設置系統屬性java.awt.headless,這裏設置爲true,表示運行在服務器端,在沒有顯示器和鼠標鍵盤的模式下工做,模擬輸入輸出設備功能

第7行經過getRunListeners方法獲取SpringApplicationRunListeners對象,這個對象是一個SpringBoot事件廣播器的管理者,它能夠包含多個SpringApplicationRunListener,SpringApplication類中使用它們來間接調用ApplicationListener

在classpath下的JAR文件中包含的/META/spring.factories文件裏找到org.springframework.boot.SpringApplicationRunListener對應的屬性,而後實例化並排序

在jar:file:/C:/Users/guiqingqing/.m2/repository/org/springframework/boot/spring-boot/1.5.6.RELEASE/spring-boot-1.5.6.RELEASE.jar!/META-INF/spring.factories文件找到以下配置

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

這裏只有一個,就是EventPublishingRunListener,而後做爲參數調用SpringApplicationRunListeners的構造函數,建立對象並返回,EventPublishingRunListener對象的實例被添加到SpringApplicationRunListeners對象的listeners屬性中

看一下EventPublishingRunListener的構造函數

複製代碼
1 public EventPublishingRunListener(SpringApplication application, String[] args) {
2     this.application = application;
3     this.args = args;
4     this.initialMulticaster = new SimpleApplicationEventMulticaster();
5     for (ApplicationListener<?> listener : application.getListeners()) {
6         this.initialMulticaster.addApplicationListener(listener);
7     }
8 }
複製代碼

內部會建立一個Spring廣播器SimpleApplicationEventMulticaster對象,它其實是一個事件廣播器,EventPublishingRunListener實現了SpringApplicationRunListener接口,咱們看一下SpringApplicationRunListener接口的定義

複製代碼
 1 public interface SpringApplicationRunListener {
 2     void starting();
 3 
 4     void environmentPrepared(ConfigurableEnvironment environment);
 5 
 6     void contextPrepared(ConfigurableApplicationContext context);
 7 
 8     void contextLoaded(ConfigurableApplicationContext context);
 9 
10     void finished(ConfigurableApplicationContext context, Throwable exception);
11 }
複製代碼

該接口規定了SpringBoot的生命週期,在各個生命週期廣播相應的事件,調用實際的ApplicationListener的onApplicationEvent方法

回到SpringApplication的run方法

第8行調用listeners的starting方法

1 public void starting() {
2     for (SpringApplicationRunListener listener : this.listeners) {
3         listener.starting();
4     }
5 }

該方法遍歷全部的listeners,固然這裏只有一個(EventPublishingRunListener),調用它的starting方法

1 @Override
2 @SuppressWarnings("deprecation")
3 public void starting() {
4     this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
5 }

這裏會使用廣播器(SimpleApplicationEventMulticaster)去廣播事件,建立一個ApplicationStartedEvent對象做爲參數

複製代碼
 1 @Override
 2 public void multicastEvent(ApplicationEvent event) {
 3     multicastEvent(event, resolveDefaultEventType(event));
 4 }
 5 
 6 @Override
 7 public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
 8     ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 9     for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
10         Executor executor = getTaskExecutor();
11         if (executor != null) {
12             executor.execute(new Runnable() {
13                 @Override
14                 public void run() {
15                     invokeListener(listener, event);
16                 }
17             });
18         }
19         else {
20             invokeListener(listener, event);
21         }
22     }
23 }
複製代碼

能夠看出multicastEvent方法會找到ApplicationListener的集合,而後依次調用invokeListener方法,而invokeListener方法內部則會調用ApplicationListener對象的onApplicationEvent方法

第10行建立一個DefaultApplicationArguments對象,它持有着args參數,就是main函數傳進來的參數

第12行調用prepareEnvironment方法,該方法準備運行的環境,好比開發環境dev,測試環境test,仍是生產環境prd,而後根據環境解析不一樣的配置文件

複製代碼
 1 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
 2     // Create and configure the environment
 3     ConfigurableEnvironment environment = getOrCreateEnvironment();
 4     configureEnvironment(environment, applicationArguments.getSourceArgs());
 5     listeners.environmentPrepared(environment);
 6     if (!this.webEnvironment) {
 7         environment = new EnvironmentConverter(getClassLoader())
 8                 .convertToStandardEnvironmentIfNecessary(environment);
 9     }
10     return environment;
11 }
複製代碼

getOrCreateEnvironment方法會檢查以前設置的webEnvironment屬性,若是是web程序,那麼建立一個StandardServletEnvironment對象並返回,若是不是web程序,那麼建立一個StandardEnvironment對象並返回

接下來是configureEnvironment方法,該方法會對上一步返回的environment作進一步的配置,調用configurePropertySources和configureProfiles,好比main函數傳進來參數"--spring.profiles.active=dev",那麼會在configurePropertySources方法裏被添加到CommandLineArgs對象的optionArgs屬性,不是以"--"開頭的參數會被添加到nonOptionArgs屬性。而configureProfiles方法這個時候就會獲取到dev做爲當前有效的profile並添加到environment的activeProfiles屬性中,本例不傳參數,因此基本上configureEnvironment方法什麼都不作

而後調用listeners的environmentPrepared方法,發佈一個ApplicationEnvironmentPreparedEvent事件,經過事件廣播器,依次調用每一個ApplicationListener對象的onApplicationEvent方法,這裏咱們重點分析ConfigFileApplicationListener的onApplicationEvent方法

複製代碼
 1 @Override
 2 public void onApplicationEvent(ApplicationEvent event) {
 3     if (event instanceof ApplicationEnvironmentPreparedEvent) {
 4         onApplicationEnvironmentPreparedEvent(
 5                 (ApplicationEnvironmentPreparedEvent) event);
 6     }
 7     if (event instanceof ApplicationPreparedEvent) {
 8         onApplicationPreparedEvent(event);
 9     }
10 }
複製代碼
複製代碼
1 private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
2     List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
3     postProcessors.add(this);
4     AnnotationAwareOrderComparator.sort(postProcessors);
5     for (EnvironmentPostProcessor postProcessor : postProcessors) {
6         postProcessor.postProcessEnvironment(event.getEnvironment(),
7                 event.getSpringApplication());
8     }
9 }
複製代碼

首先loadPostProcessors方法會去classpath下的JAR文件中包含的/META/spring.factories文件裏找到org.springframework.boot.env.EnvironmentPostProcessor對應的屬性,而後實例化並排序,設置到postProcessors變量,在把當前對象(ConfigFileApplicationListener的實例)也添加到postProcessors變量中,而後對postProcessors排序,遍歷postProcessors,依次調用它們的postProcessEnvironment方法。咱們來看ConfigFileApplicationListener的postProcessEnvironment方法

複製代碼
1 @Override
2 public void postProcessEnvironment(ConfigurableEnvironment environment,
3         SpringApplication application) {
4     addPropertySources(environment, application.getResourceLoader());
5     configureIgnoreBeanInfo(environment);
6     bindToSpringApplication(environment, application);
7 }
複製代碼

addPropertySources方法最終會調用到ConfigFileApplicationListener的內部類Loader的load方法,這個方法是解析咱們配置的application.yml和application-dev.yml文件的實現所在

複製代碼
 1 public void load() {
 2     this.propertiesLoader = new PropertySourcesLoader();
 3     this.activatedProfiles = false;
 4     this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
 5     this.processedProfiles = new LinkedList<Profile>();
 6 
 7     // Pre-existing active profiles set via Environment.setActiveProfiles()
 8     // are additional profiles and config files are allowed to add more if
 9     // they want to, so don't call addActiveProfiles() here.
10     Set<Profile> initialActiveProfiles = initializeActiveProfiles();
11     this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
12     if (this.profiles.isEmpty()) {
13         for (String defaultProfileName : this.environment.getDefaultProfiles()) {
14             Profile defaultProfile = new Profile(defaultProfileName, true);
15             if (!this.profiles.contains(defaultProfile)) {
16                 this.profiles.add(defaultProfile);
17             }
18         }
19     }
20 
21     // The default profile for these purposes is represented as null. We add it
22     // last so that it is first out of the queue (active profiles will then
23     // override any settings in the defaults when the list is reversed later).
24     this.profiles.add(null);
25 
26     while (!this.profiles.isEmpty()) {
27         Profile profile = this.profiles.poll();
28         for (String location : getSearchLocations()) {
29             if (!location.endsWith("/")) {
30                 // location is a filename already, so don't search for more
31                 // filenames
32                 load(location, null, profile);
33             }
34             else {
35                 for (String name : getSearchNames()) {
36                     load(location, name, profile);
37                 }
38             }
39         }
40         this.processedProfiles.add(profile);
41     }
42 
43     addConfigurationProperties(this.propertiesLoader.getPropertySources());
44 }
複製代碼

25行以前基本上是一些變量的實例化,會往profiles集合中添加default和null,直接跳過,接下來從profiles集合中取出對應的值進行解析,首先取出null

第28行getSearchLocations方法,首先會檢查當前環境是否配置了spring.config.location,若是沒有,那麼會使用默認的搜索路徑classpath:/,classpath:/config/,file:./,file:./config/,這裏調用了Collections.reverse方法進行倒序,也就是先查找file:./config/,在查找file:./,再是classpath:/config/,最後查找classpath:/。筆者的application.yml和application-dev.yml都是直接在src/main/resources目錄下,因此對應的是在location爲classpath:/時調用load(String location, String name, Profile profile)方法(在沒有配置spring.config.name的狀況下,name默認爲application)

複製代碼
 1 private void load(String location, String name, Profile profile) {
 2     String group = "profile=" + (profile == null ? "" : profile);
 3     if (!StringUtils.hasText(name)) {
 4         // Try to load directly from the location
 5         loadIntoGroup(group, location, profile);
 6     }
 7     else {
 8         // Search for a file with the given name
 9         for (String ext : this.propertiesLoader.getAllFileExtensions()) {
10             if (profile != null) {
11                 // Try the profile-specific file
12                 loadIntoGroup(group, location + name + "-" + profile + "." + ext,
13                         null);
14                 for (Profile processedProfile : this.processedProfiles) {
15                     if (processedProfile != null) {
16                         loadIntoGroup(group, location + name + "-"
17                                 + processedProfile + "." + ext, profile);
18                     }
19                 }
20                 // Sometimes people put "spring.profiles: dev" in
21                 // application-dev.yml (gh-340). Arguably we should try and error
22                 // out on that, but we can be kind and load it anyway.
23                 loadIntoGroup(group, location + name + "-" + profile + "." + ext,
24                         profile);
25             }
26             // Also try the profile-specific section (if any) of the normal file
27             loadIntoGroup(group, location + name + "." + ext, profile);
28         }
29     }
30 }
複製代碼

調用this.propertiesLoader.getAllFileExtensions()方法獲取配置文件後綴名,分別是在PropertiesPropertySourceLoader類和YamlPropertySourceLoader類中getFileExtensions方法定義的

1 public class PropertiesPropertySourceLoader implements PropertySourceLoader {
2     @Override
3     public String[] getFileExtensions() {
4         return new String[] { "properties", "xml" };
5     }
6 }
1 public class YamlPropertySourceLoader implements PropertySourceLoader {
2     @Override
3     public String[] getFileExtensions() {
4         return new String[] { "yml", "yaml" };
5     }
6 }

第23行調用的loadIntoGroup方法裏又調用doLoadIntoGroup方法,這個方法會檢查配置文件是否存在,好比file:./config/下不存在,或是classpath:/下的application.properties不存在,最後發現classpath:/下的application.yml存在,而後對其進行解析

複製代碼
 1 public PropertySource<?> load(Resource resource, String group, String name,
 2             String profile) throws IOException {
 3     if (isFile(resource)) {
 4         String sourceName = generatePropertySourceName(name, profile);
 5         for (PropertySourceLoader loader : this.loaders) {
 6             if (canLoadFileExtension(loader, resource)) {
 7                 PropertySource<?> specific = loader.load(sourceName, resource,
 8                         profile);
 9                 addPropertySource(group, specific, profile);
10                 return specific;
11             }
12         }
13     }
14     return null;
15 }
複製代碼

這裏有2個loader,PropertiesPropertySourceLoader和YamlPropertySourceLoader,先檢查是否該由此loader去解析,檢查的規則就是看文件後綴名是否在getFileExtensions方法返回的數組中存在,很顯然,這裏應該由YamlPropertySourceLoader去解析,筆者的application.yml很簡單,內容以下

spring:
  profiles:
    active: dev

解析完成以後,調用了一個handleProfileProperties方法,這個方法裏又會調用maybeActivateProfiles方法,此方法的addProfiles方法會把解析到的dev添加到profiles中去,removeUnprocessedDefaultProfiles方法會刪除以前添加的default(此時只剩下dev),若是配置了include,也會被解析

而後再次遍歷file:./config/,file:./,classpath:/config/,classpath:/,此次是查找到application-dev.yml並解析,過程就不重複分析了

回到SpringApplication的run方法

第14行調用printBanner方法,打印SpringBoot的LOGO

複製代碼
 1 private Banner printBanner(ConfigurableEnvironment environment) {
 2     if (this.bannerMode == Banner.Mode.OFF) {
 3         return null;
 4     }
 5     ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
 6             : new DefaultResourceLoader(getClassLoader());
 7     SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
 8             resourceLoader, this.banner);
 9     if (this.bannerMode == Mode.LOG) {
10         return bannerPrinter.print(environment, this.mainApplicationClass, logger);
11     }
12     return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
13 }
複製代碼

方法內部會建立一個SpringApplicationBannerPrinter的對象,並調用print方法

1 public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
2     Banner banner = getBanner(environment, this.fallbackBanner);
3     banner.printBanner(environment, sourceClass, out);
4     return new PrintedBanner(banner, sourceClass);
5 }

經過getBanner方法獲取Banner對象

複製代碼
 1 private Banner getBanner(Environment environment, Banner definedBanner) {
 2     Banners banners = new Banners();
 3     banners.addIfNotNull(getImageBanner(environment));
 4     banners.addIfNotNull(getTextBanner(environment));
 5     if (banners.hasAtLeastOneBanner()) {
 6         return banners;
 7     }
 8     if (this.fallbackBanner != null) {
 9         return this.fallbackBanner;
10     }
11     return DEFAULT_BANNER;
12 }
複製代碼

第一步,調用Banners的無參構造建立一個Banners的對象,而後咱們先看一下SpringApplicationBannerPrinter類裏定義的幾個靜態屬性

複製代碼
1 static final String BANNER_LOCATION_PROPERTY = "banner.location";
2 
3 static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
4 
5 static final String DEFAULT_BANNER_LOCATION = "banner.txt";
6 
7 static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
複製代碼

先看是否配置了系統屬性banner.image.location,沒有配置則在classpath中查找banner.gif,banner.jpg,banner.png,若是找到,則建立一個ImageBanner對象並添加到Banners對象的banners屬性中,該屬性是一個List類型,接下來看是否配置了banner.location屬性,若是沒有,則用默認的banner.txt,而後在classpath中查找banner.txt,若是找到,則建立一個ResourceBanner對象並添加到Banners對象的banners屬性中,最後,若是Banners對象的banners不爲空,也就是至少找到了banner.gif,banner.jpg,banner.png,banner.txt其中的一個,那麼返回該Banners對象,不然返回默認的SpringBootBanner對象

接下來根據返回的不一樣Banners類型的對象調用具體的printBanner實現方法,因此若是要想打印自定義的LOGO,要麼你在classpath下添加圖片,要麼添加banner.txt的文本文件,默認的SpringBootBanner會打印以下圖

回到SpringApplication的run方法

第15行調用createApplicationContext方法,該方法建立SpringBoot的上下文

複製代碼
 1 protected ConfigurableApplicationContext createApplicationContext() {
 2     Class<?> contextClass = this.applicationContextClass;
 3     if (contextClass == null) {
 4         try {
 5             contextClass = Class.forName(this.webEnvironment
 6                     ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
 7         }
 8         catch (ClassNotFoundException ex) {
 9             throw new IllegalStateException(
10                     "Unable create a default ApplicationContext, "
11                             + "please specify an ApplicationContextClass",
12                     ex);
13         }
14     }
15     return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
16 }
複製代碼

經過判斷當前是不是web環境決定建立什麼類,若是是web程序,那麼建立org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext的實例,不然建立org.springframework.context.annotation.AnnotationConfigApplicationContext的實例,這些都是定義在SpringApplication類的靜態屬性中

第16行建立FailureAnalyzers的對象,FailureAnalyzers的構造函數裏調用了loadFailureAnalyzers方法,仍是老規矩,在classpath下的JAR文件中包含的/META/spring.factories文件中找到org.springframework.boot.diagnostics.FailureAnalyzer對應的屬性,實例化並排序,賦值給FailureAnalyzers對象的analyzers屬性,主要是用來處理啓動時發生一些異常時的一些分析

第17行調用prepareContext方法,準備上下文

複製代碼
 1 private void prepareContext(ConfigurableApplicationContext context,
 2             ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
 3             ApplicationArguments applicationArguments, Banner printedBanner) {
 4     context.setEnvironment(environment);
 5     postProcessApplicationContext(context);
 6     applyInitializers(context);
 7     listeners.contextPrepared(context);
 8     if (this.logStartupInfo) {
 9         logStartupInfo(context.getParent() == null);
10         logStartupProfileInfo(context);
11     }
12 
13     // Add boot specific singleton beans
14     context.getBeanFactory().registerSingleton("springApplicationArguments",
15             applicationArguments);
16     if (printedBanner != null) {
17         context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
18     }
19 
20     // Load the sources
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 }
複製代碼

先給上下文設置環境,而後調用postProcessApplicationContext方法設置上下文的beanNameGenerator和resourceLoader(若是SpringApplication有的話)

接下來調用applyInitializers方法,拿到以前實例化SpringApplication對象的時候設置的ApplicationContextInitializer,調用它們的initialize方法,對上下文作初始化

介紹幾個ApplicationContextInitializer,其餘的不過多介紹,有興趣的能夠本身DEBUG

DelegatingApplicationContextInitializer: 從環境中(配置的context.initializer.classes)取出全部的ApplicationContextInitializer並執行

ContextIdApplicationContextInitializer: 設置上下文的id(name + ":" + profiles + ":" + index),筆者調試的id爲(application:dev:8080)

下面一行調用listeners的contextPrepared方法,跟以前調用listeners的starting方法同樣,只是EventPublishingRunListener的contextPrepared方法是個空實現

而後打印啓動日誌

2017-09-02 22:08:07.196 [INFO ][main]:com.dylan.java.springboot.template.Application[logStarting:48] - Starting Application on Dylan-PC with PID 5764 (F:\Dylan\workspace\template\target\classes started by Dylan in F:\Dylan\workspace\template)
2017-09-02 22:08:12.388 [INFO ][main]:com.dylan.java.springboot.template.Application[logStartupProfileInfo:597] - The following profiles are active: dev

打印完日誌往上下文的beanFactory中註冊一個singleton的bean,bean的名字是springApplicationArguments,bean的實例是以前實例化的ApplicationArguments對象

若是以前獲取的printedBanner不爲空,那麼往上下文的beanFactory中註冊一個singleton的bean,bean的名字是springBootBanner,bean的實例就是這個printedBanner

prepareContext方法的倒數第2行調用load方法註冊啓動類的bean定義,也就是調用SpringApplication.run(Application.class, args);的類,SpringApplication的load方法內會建立BeanDefinitionLoader的對象,並調用它的load()方法

複製代碼
1 public int load() {
2     int count = 0;
3     for (Object source : this.sources) {
4         count += load(source);
5     }
6     return count;
7 }
複製代碼

對全部的source(這裏只有一個: class com.dylan.java.springboot.template.Application)都執行一次load(Object source)方法,這個方法又會調用load(Class<?> source)方法

複製代碼
 1 private int load(Class<?> source) {
 2     if (isGroovyPresent()) {
 3         // Any GroovyLoaders added in beans{} DSL can contribute beans here
 4         if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
 5             GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
 6                     GroovyBeanDefinitionSource.class);
 7             load(loader);
 8         }
 9     }
10     if (isComponent(source)) {
11         this.annotatedReader.register(source);
12         return 1;
13     }
14     return 0;
15 }
複製代碼

因爲@SpringBootApplication註解繼承了@SpringBootConfiguration註解,@SpringBootConfiguration註解繼承了@Configuration註解,@Configuration註解又繼承了@Component註解,因此上面代碼的第10行返回true,因而執行第11行和第12行代碼

複製代碼
 1 class BeanDefinitionLoader {
 2     private final Object[] sources;
 3 
 4     private final AnnotatedBeanDefinitionReader annotatedReader;
 5 
 6     private final XmlBeanDefinitionReader xmlReader;
 7 
 8     private BeanDefinitionReader groovyReader;
 9 
10     ...
11 }
複製代碼

能夠看出BeanDefinitionLoader中有多個加載BeanDefinition的Reader類,這裏針對@SpringBootApplication註解使用了annotatedReader,調用register方法,由於啓動類沒有@Conditional註解,因此不能跳過註冊的步驟

那麼就老老實實的註冊該BeanDefinition吧,沒有設置bean是singleton仍是prototype,那麼默認使用singleton,而bean的名字,則默認是把類名的首字母變小寫,也就是application,而後檢查是否有@Lazy、@Primary、@DependsOn註解並設置AnnotatedBeanDefinition的屬性,若是是AbstractBeanDefinition,還要檢查是否有@Role、@Description註解並設置其屬性,最後經過BeanDefinitionReaderUtils類的registerBeanDefinition方法註冊BeanDefinition

最後調用listeners的contextLoaded方法,說明上下文已經加載,該方法先找到全部的ApplicationListener,遍歷這些listener,若是該listener繼承了ApplicationContextAware類,那麼在這一步會調用它的setApplicationContext方法,設置context

遍歷完ApplicationListener以後,建立ApplicationPreparedEvent事件對象,並廣播出去,也就是調用全部ApplicationListener的onApplicationEvent方法

最後再回到SpringApplication的run方法,第19行調用refreshContext方法

 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);
 8     listeners.starting();
 9     try {
10         ApplicationArguments applicationArguments = new DefaultApplicationArguments(
11                 args);
12         ConfigurableEnvironment environment = prepareEnvironment(listeners,
13                 applicationArguments);
14         Banner printedBanner = printBanner(environment);
15         context = createApplicationContext();
16         analyzers = new FailureAnalyzers(context);
17         prepareContext(context, environment, listeners, applicationArguments,
18                 printedBanner);
19         refreshContext(context);
20         afterRefresh(context, applicationArguments);
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 }
複製代碼

第19行調用refreshContext方法,refreshContext方法又調用了refresh方法,而refresh方法又調用了context父類AbstractApplicationContext(真正的實現類是EmbeddedWebApplicationContext)的refresh方法,接着又調用AbstractApplicationContext的refresh方法

AbstractApplicationContext的refresh方法是本篇分析的重點,假如你經過ApplicationContext apc = new ClassPathXmlApplicationContext("beans.xml");的方式啓動Spring,也是會調用這個refresh方法來初始化容器的,它主要是完成配置類的解析,各類BeanFactoryPostProcessor和BeanPostProcessor的註冊,內置容器(tomcat)構造,國際化配置初始化,實例化非延遲加載的單例bean等

複製代碼
 1 @Override
 2 public void refresh() throws BeansException, IllegalStateException {
 3     synchronized (this.startupShutdownMonitor) {
 4         // Prepare this context for refreshing.
 5         prepareRefresh();
 6 
 7         // Tell the subclass to refresh the internal bean factory.
 8         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 9 
10         // Prepare the bean factory for use in this context.
11         prepareBeanFactory(beanFactory);
12 
13         try {
14             // Allows post-processing of the bean factory in context subclasses.
15             postProcessBeanFactory(beanFactory);
16 
17             // Invoke factory processors registered as beans in the context.
18             invokeBeanFactoryPostProcessors(beanFactory);
19 
20             // Register bean processors that intercept bean creation.
21             registerBeanPostProcessors(beanFactory);
22 
23             // Initialize message source for this context.
24             initMessageSource();
25 
26             // Initialize event multicaster for this context.
27             initApplicationEventMulticaster();
28 
29             // Initialize other special beans in specific context subclasses.
30             onRefresh();
31 
32             // Check for listener beans and register them.
33             registerListeners();
34 
35             // Instantiate all remaining (non-lazy-init) singletons.
36             finishBeanFactoryInitialization(beanFactory);
37 
38             // Last step: publish corresponding event.
39             finishRefresh();
40         }
41 
42         catch (BeansException ex) {
43             if (logger.isWarnEnabled()) {
44                 logger.warn("Exception encountered during context initialization - " +
45                         "cancelling refresh attempt: " + ex);
46             }
47 
48             // Destroy already created singletons to avoid dangling resources.
49             destroyBeans();
50 
51             // Reset 'active' flag.
52             cancelRefresh(ex);
53 
54             // Propagate exception to caller.
55             throw ex;
56         }
57 
58         finally {
59             // Reset common introspection caches in Spring's core, since we
60             // might not ever need metadata for singleton beans anymore...
61             resetCommonCaches();
62         }
63     }
64 }
複製代碼

第3行使用synchronized關鍵字拿到startupShutdownMonitor的鎖,close方法的第一行也是先獲取該鎖,說明啓動容器和關閉容器都是同步操做的,是線程安全的

第5行調用prepareRefresh方法,該方法主要是作些準備工做,例如清空緩存,記錄容器啓動的時間,設置closed爲false,設置active爲true,初始化屬性信息,驗證必要的屬性

這裏的初始化屬性信息和驗證必要的屬性方法默認是不作任何事情的,但有時候系統運行須要讀取某些系統屬性,沒有這些屬性,系統將運行不正常,這個時候,在建立容器以前就要對這些必須的屬性作個驗證,好比本身實現一個ApplicationContext並覆蓋initPropertySources方法

1 public class MyApplicationContext extends AnnotationConfigEmbeddedWebApplicationContext {
2     protected void initPropertySources() {
3         getEnvironment().setRequiredProperties("dylan.required");
4     }
5 }

而後SpringApplication啓動的方式也改下

複製代碼
1 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
2 public class Application {
3     public static void main(String[] args) {
4         SpringApplication springApplication = new SpringApplication(new Object[] {Application.class});
5         springApplication.setApplicationContextClass(MyApplicationContext.class);
6         springApplication.run(args);
7     }
8 }
複製代碼

那麼當運行到initPropertySources();這個方法時,會調用自定義的ApplicationContext的initPropertySources方法,向environment中添加必要的屬性,而運行getEnvironment().validateRequiredProperties();這個方法的時候,就會檢查是否配置了"dylan.required"屬性,若是沒有配置,那麼會拋出異常,容器啓動失敗

第8行經過obtainFreshBeanFactory方法獲取到beanFactory

複製代碼
1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
2     refreshBeanFactory();
3     ConfigurableListableBeanFactory beanFactory = getBeanFactory();
4     if (logger.isDebugEnabled()) {
5         logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
6     }
7     return beanFactory;
8 }
複製代碼

refreshBeanFactory方法把refreshed屬性設置成true,表示已經刷新了,下次再刷新就會拋出異常,不容許重複刷新,而後給beanFactory設置serializationId,就是以前經過ContextIdApplicationContextInitializer生成的id

getBeanFactory方法拿到ApplicationContext的beanFactory(以前建立上下文的時候,也就是調用createApplicationContext方法,GenericApplicationContext的構造函數裏會實例化一個DefaultListableBeanFactory對象),而後返回

1 public GenericApplicationContext() {
2     this.beanFactory = new DefaultListableBeanFactory();
3 }

第11行調用prepareBeanFactory方法,該方法對beanFactory進行一系列的設置

複製代碼
 1 protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 2     // Tell the internal bean factory to use the context's class loader etc.
 3     beanFactory.setBeanClassLoader(getClassLoader());
 4     beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
 5     beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
 6 
 7     // Configure the bean factory with context callbacks.
 8     beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
 9     beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
10     beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
11     beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
12     beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
13     beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
14     beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
15 
16     // BeanFactory interface not registered as resolvable type in a plain factory.
17     // MessageSource registered (and found for autowiring) as a bean.
18     beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
19     beanFactory.registerResolvableDependency(ResourceLoader.class, this);
20     beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
21     beanFactory.registerResolvableDependency(ApplicationContext.class, this);
22 
23     // Register early post-processor for detecting inner beans as ApplicationListeners.
24     beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
25 
26     // Detect a LoadTimeWeaver and prepare for weaving, if found.
27     if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
28         beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
29         // Set a temporary ClassLoader for type matching.
30         beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
31     }
32 
33     // Register default environment beans.
34     if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
35         beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
36     }
37     if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
38         beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
39     }
40     if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
41         beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
42     }
43 }
複製代碼

首先設置用於加載bean的類加載器,再設置能夠解析bean表達式的表達式解析器(使用"#{xxx}"配置的屬性),而後添加屬性註冊器,並添加了一個BeanPostProcessor - ApplicationContextAwareProcessor的實例

接着設置了6個忽略自動注入的接口(EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware),爲何要忽略呢?由於上一步添加的BeanPostProcessor - ApplicationContextAwareProcessor,BeanPostProcessor會在會在bean初始化先後調用相應的方法,咱們看一下ApplicationContextAwareProcessor的代碼

複製代碼
 1 @Override
 2 public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
 3     AccessControlContext acc = null;
 4 
 5     if (System.getSecurityManager() != null &&
 6             (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || 7 bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || 8 bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
 9         acc = this.applicationContext.getBeanFactory().getAccessControlContext();
10     }
11 
12     if (acc != null) {
13         AccessController.doPrivileged(new PrivilegedAction<Object>() {
14             @Override
15             public Object run() {
16                 invokeAwareInterfaces(bean);
17                 return null;
18             }
19         }, acc);
20     }
21     else {
22         invokeAwareInterfaces(bean);
23     }
24 
25     return bean;
26 }
27 
28 private void invokeAwareInterfaces(Object bean) {
29     if (bean instanceof Aware) {
30         if (bean instanceof EnvironmentAware) {
31             ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
32         }
33         if (bean instanceof EmbeddedValueResolverAware) {
34             ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
35         }
36         if (bean instanceof ResourceLoaderAware) {
37             ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
38         }
39         if (bean instanceof ApplicationEventPublisherAware) {
40             ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
41         }
42         if (bean instanceof MessageSourceAware) {
43             ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
44         }
45         if (bean instanceof ApplicationContextAware) {
46             ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
47         }
48     }
49 }
複製代碼

從上面紅色標註的代碼能夠看出,在ApplicationContextAwareProcessor的postProcessBeforeInitialization方法裏恰好幫咱們作了這6個被忽略的Aware接口須要感知的動做

接下來的4行設置了幾個自動裝配的特殊規則,若是注入的是BeanFactory類型,則注入beanFactory對象,若是是ResourceLoader、ApplicationEventPublisher、ApplicationContext類型,則注入當前對象(Spring上下文 - context)

而後又註冊了一個BeanPostProcessor - ApplicationListenerDetector,咱們看下ApplicationListenerDetector的代碼

1 @Override
2 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
3     if (this.applicationContext != null) {
4         this.singletonNames.put(beanName, beanDefinition.isSingleton());
5     }
6 }

postProcessMergedBeanDefinition方法在何時調用的呢?DEBUG跟進代碼可知,是doCreateBean -> applyMergedBeanDefinitionPostProcessors -> postProcessMergedBeanDefinition,該方法會往singletonNames屬性添加以beanName爲key、該bean定義是否爲單例爲value的鍵值對

複製代碼
 1 @Override
 2 public Object postProcessAfterInitialization(Object bean, String beanName) {
 3     if (this.applicationContext != null && bean instanceof ApplicationListener) {
 4         // potentially not detected as a listener by getBeanNamesForType retrieval
 5         Boolean flag = this.singletonNames.get(beanName);
 6         if (Boolean.TRUE.equals(flag)) {
 7             // singleton bean (top-level or inner): register on the fly
 8             this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
 9         }
10         else if (Boolean.FALSE.equals(flag)) {
11             if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
12                 // inner bean with other scope - can't reliably process events
13                 logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
14                         "but is not reachable for event multicasting by its containing ApplicationContext " +
15                         "because it does not have singleton scope. Only top-level listener beans are allowed " +
16                         "to be of non-singleton scope.");
17             }
18             this.singletonNames.remove(beanName);
19         }
20     }
21     return bean;
22 }
複製代碼

該BeanPostProcessor在bean初始化後,會檢查該bean是不是ApplicationListener,若是是,那麼會驗證它是不是單例,若是不是單例,那麼刪除singletonNames中對應的key,這也正如它的名字同樣 - ApplicationListener探測器

接着往下看

若是beanFactory中有名字是loadTimeWeaver的bean,那麼註冊一個BeanPostProcessor - LoadTimeWeaverAwareProcessor

LoadTimeWeaverAwareProcessor的代碼

複製代碼
 1 @Override
 2 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 3     if (bean instanceof LoadTimeWeaverAware) {
 4         LoadTimeWeaver ltw = this.loadTimeWeaver;
 5         if (ltw == null) {
 6             Assert.state(this.beanFactory != null,
 7                     "BeanFactory required if no LoadTimeWeaver explicitly specified");
 8             ltw = this.beanFactory.getBean(
 9                     ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
10         }
11         ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
12     }
13     return bean;
14 }
複製代碼

該BeanPostProcessor只對LoadTimeWeaverAware類型的bean作特殊處理,查了下Spring中實現LoadTimeWeaverAware接口的類,只有AspectJWeavingEnabler,在Spring中調用AspectJWeavingEnabler時,this.loadTimeWeaver還沒有被初始化,那麼會直接調用this.beanFactory.getBean方法獲取對應的DefaultContextLoadTimeWeaver類型的bean,並設置到AspectJWeavingEnabler類型bean的loadTimeWeaver屬性中。當容器中註冊了loadTimeWeaver以後會給容器設置一個ContextTypeMatchClassLoader類型的臨時類加載器,在織入切面時只有在bean實例化時織入切面纔有意義,在進行一些類型比較或者校驗的時候,好比判斷一個bean是不是FactoryBean、BPP、BFPP,這時候不涉及到實例化,因此作字節碼轉換沒有任何意義,並且還會增長無謂的性能消耗,因此在進行這些類型比較時使用這個臨時的類加載器執行類加載。這個臨時的類加載器會在容器初始化快結束時,容器bean實例化以前被清掉,代碼在AbstractApplicationContext類的finishBeanFactoryInitialization方法

複製代碼
 1 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
 2     ...
 3 
 4     // Stop using the temporary ClassLoader for type matching.
 5     beanFactory.setTempClassLoader(null);
 6 
 7     // Allow for caching all bean definition metadata, not expecting further changes.
 8     beanFactory.freezeConfiguration();
 9 
10     // Instantiate all remaining (non-lazy-init) singletons.
11     beanFactory.preInstantiateSingletons();
12 }
複製代碼

設置完loadTimeWeaver後,再判斷當前beanFactory是否含有名字爲environment的bean,若是沒有,那麼把當前的environment註冊進去

接着判斷當前beanFactory是否含有名字爲systemProperties的bean,若是沒有,那麼把System.getProperties()註冊進去

最後判斷當前beanFactory是否含有名字爲systemEnvironment的bean,若是沒有,那麼把System.getenv()註冊進去

至此refresh的prepareBeanFactory方法結束,再往下看refresh方法

第15行調用postProcessBeanFactory方法,該方法最終會調用到EmbeddedWebApplicationContext的postProcessBeanFactory方法,給beanFactory添加一個WebApplicationContextServletContextAwareProcessor類型的BeanPostProcessor,並添加了一個忽略裝配的接口ServletContextAware,原理跟以前prepareBeanFactory方法分析的同樣,WebApplicationContextServletContextAwareProcessor繼承了ServletContextAwareProcessor,而ServletContextAwareProcessor的postProcessBeforeInitialization方法代碼已經作了ServletContextAware該作的事情

複製代碼
 1 @Override
 2 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 3     if (getServletContext() != null && bean instanceof ServletContextAware) {
 4         ((ServletContextAware) bean).setServletContext(getServletContext());
 5     }
 6     if (getServletConfig() != null && bean instanceof ServletConfigAware) {
 7         ((ServletConfigAware) bean).setServletConfig(getServletConfig());
 8     }
 9     return bean;
10 }
複製代碼

檢查basePackages屬性,若是設置了會使用ClassPathBeanDefinitionScanner掃描basePackages並註冊被掃描到的bean

檢查annotatedClasses屬性,若是設置了會使用AnnotatedBeanDefinitionReader註冊這些類型的bean

第18行調用invokeBeanFactoryPostProcessors方法,該方法主要是執行bean工廠的後置處理

首先,獲取上下的beanFactoryPostProcessors,筆者在DEBUG的時候,發現此時有3個

 

這3個BeanFactoryPostProcessor是何時加進去的呢,前兩個其實就是以前分析過的SpringApplication的run方法中調用prepareContext設置上下文的時候,其中有一步applyInitializers,在調用ConfigurationWarningsApplicationContextInitializer和SharedMetadataReaderFactoryContextInitializer的initialize方法中添加進去的,至於第3個,則是在listeners.contextLoaded(context);方法中被添加的,contextLoaded會發布ApplicationPreparedEvent事件,恰好被ConfigFileApplicationListener監聽到,而ConfigFileApplicationListener的onApplicationEvent的方法中又會調用onApplicationPreparedEvent方法,沒錯,就是這個方法添加了PropertySourceOrderingPostProcessor

迴歸正題,獲取到上下文的beanFactoryPostProcessors後,調用了PostProcessorRegistrationDelegate的靜態方法invokeBeanFactoryPostProcessors,下面來詳細分析一下這個方法

首先判斷beanFactory是否是BeanDefinitionRegistry類型的,若是beanFactory是BeanDefinitionRegistry類型的實現類,那麼

  - 第一步,遍歷上下文獲取到的beanFactoryPostProcessors,若是是BeanDefinitionRegistryPostProcessor類型的,那麼調用該postProcessor的postProcessBeanDefinitionRegistry方法

  - 第二步,在beanFactory中查詢BeanDefinitionRegistryPostProcessor類型的beanName數組,若是是實現了PriorityOrdered接口,那麼獲取bean並添加到priorityOrderedPostProcessors集合中,對其排序,並調用它的postProcessBeanDefinitionRegistry方法

  - 第三步,在beanFactory中查詢BeanDefinitionRegistryPostProcessor類型的beanName數組,若是以前沒有處理過該bean(即沒有實現PriorityOrdered接口)並實現了Ordered接口,那麼獲取bean並添加到orderedPostProcessors集合中,對其排序,並調用它的postProcessBeanDefinitionRegistry方法

  - 第四步,在beanFactory中查詢BeanDefinitionRegistryPostProcessor類型的beanName數組,若是以前沒有處理過該bean(即沒有實現PriorityOrdered和Ordered接口),那麼獲取bean,並調用它的postProcessBeanDefinitionRegistry方法

  - 第五步,對上面調用了postProcessBeanDefinitionRegistry的後置處理器,調用postProcessBeanFactory方法

  - 第六步,對在第一步中講到的遍歷beanFactoryPostProcessors後,不是BeanDefinitionRegistryPostProcessor類型的,那麼調用它們的postProcessBeanFactory方法

若是beanFactory不是BeanDefinitionRegistry類型的實現類,那麼直接遍歷上下文獲取到的beanFactoryPostProcessors,調用它們的postProcessBeanFactory方法

接下來,處理BeanFactoryPostProcessor,在beanFactory中查詢BeanFactoryPostProcessor類型的beanName數組,遍歷該數組,若是以前處理過,直接跳過,不然,獲取實現了PriorityOrdered接口的bean,排序並調用它們的postProcessBeanFactory方法,再獲取實現了Ordered接口的bean,排序並調用它們的postProcessBeanFactory方法,最後,獲取沒有實現PriorityOrdered和Ordered接口的bean,調用它們的postProcessBeanFactory方法

方法的最後,清空了beanFactory的緩存

總結一下該方法,其實就是按照必定的順序,執行BeanFactoryPostProcessor接口的postProcessBeanFactory方法,BeanDefinitionRegistryPostProcessor接口繼承了BeanFactoryPostProcessor接口,並定義了postProcessBeanDefinitionRegistry方法

順序總結以下

一、上下文中beanFactoryPostProcessors並實現了BeanDefinitionRegistryPostProcessor接口 -> postProcessBeanDefinitionRegistry方法

二、beanFactory中實現了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor -> postProcessBeanDefinitionRegistry方法

三、beanFactory中實現了Ordered接口的BeanDefinitionRegistryPostProcessor -> postProcessBeanDefinitionRegistry方法

四、beanFactory中沒有實現PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor -> postProcessBeanDefinitionRegistry方法

五、beanFactory中實現了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor -> postProcessBeanFactory方法

六、beanFactory中實現了Ordered接口的BeanDefinitionRegistryPostProcessor -> postProcessBeanFactory方法

七、beanFactory中沒有實現PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor -> postProcessBeanFactory方法

八、上下文中beanFactoryPostProcessors沒有實現BeanDefinitionRegistryPostProcessor接口 -> postProcessBeanFactory方法

九、beanFactory中實現了PriorityOrdered接口的BeanFactoryPostProcessor -> postProcessBeanFactory方法

十、beanFactory中實現了Ordered接口的BeanFactoryPostProcessor -> postProcessBeanFactory方法

十一、beanFactory中沒有實現PriorityOrdered和Ordered接口的BeanFactoryPostProcessor -> postProcessBeanFactory方法

能夠看出,BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法要先於BeanFactoryPostProcessor接口的postProcessBeanFactory方法,它們均可以在Spring容器加載了bean的定義以後,在bean實例化以前,對bean的元數據進行修改

第21行調用registerBeanPostProcessors方法,該方法主要是註冊bean的後置處理器

調用PostProcessorRegistrationDelegate的靜態方法registerBeanPostProcessors

相關文章
相關標籤/搜索