一、前言

spring cloud大行其道的当下,如果不了解基本原理那么是很纠结的(看见的都是约定大于配置,但是原理呢?为什么要这么做?如何串联起来的?)。spring cloud是基于spring boot快速搭建的,今天咱们就看看spring boot容器启动流程(全文基于1.5.9版本)。(本文不讲解如何快速启动spring boot,那些直接官方看即可,官网文档飞机票)

二、容器启动

spring boot一般是指定容器启动main方法,然后以命令行方式启动Jar包,如下图:

 @SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

这里核心关注2个东西:

1.@SpringBootApplication注解

2.SpringApplication.run()静态方法

下面我们就分别探究这两块内容。

2.1 @SpringBootApplication注解

源码如下:

 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

核心注解:

@SpringBootConfiguration(实际就是个@Configuration):表示这是一个JavaConfig配置类,可以在这个类中自定义bean,依赖关系等。-》这个是spring-boot特有的注解,常用到。
@EnableAutoConfiguration:借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器(建议放在根包路径下,这样可以扫描子包和类)。-》这个需要详细深挖!
@ComponentScan:spring的自动扫描注解,可定义扫描范围,加载到IOC容器。-》这个不多说,spring的注解大家肯定眼熟 其中@EnableAutoConfiguration这个注解的源码:
 @SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

核心是一个EnableAutoConfigurationImportSelector类图如下:

核心方法在顶级接口ImportSelector的selectImports(),源码如下:

 @Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try { //1.从META-INF/spring-autoconfigure-metadata.properties文件中载入483条配置属性(有一些有默认值),
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);//2.获取注解属性
List<String> configurations = getCandidateConfigurations(annotationMetadata,//3.获取97个自动配置类
attributes);
configurations = removeDuplicates(configurations);//4.移除重复的
configurations = sort(configurations, autoConfigurationMetadata);//5.排序
Set<String> exclusions = getExclusions(annotationMetadata, attributes);//6.获取需要排除的
checkExcludedClasses(configurations, exclusions);//7.校验排除类
configurations.removeAll(exclusions);//8.删除所有需要排除的
configurations = filter(configurations, autoConfigurationMetadata);//9.过滤器OnClassCondition(注解中配置的当存在某类才生效)
fireAutoConfigurationImportEvents(configurations, exclusions);//10.触发自动配置导入监听事件
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}

这里注意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的配置。

 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {//话说这里2个入参没啥用啊...谁来给我解释一下...
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
//返回的是EnableAutoConfiguration类
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

实际获取了什么?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注解把不满足条件的过滤掉

 private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = configurations.toArray(new String[configurations.size()]);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
//获取需要过滤的自动配置导入拦截器,spring.factories配置中就一个:org.springframework.boot.autoconfigure.condition.OnClassCondition
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
skipped = true;
}
}
}
if (!skipped) {//多条件只要有一个不匹配->skipped = true,全部匹配-》skipped = false->直接返回
return configurations;
}
List<String> result = new ArrayList<String>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {//匹配-》不跳过-》添加进result
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
return new ArrayList<String>(result);
}

2.2 SpringApplication.run()静态方法

SpringApplication.run

 public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);//1.获取监听器
listeners.starting();-->启动!
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,//2.准备好环境,触发ApplicationEnvironmentPreparedEvent事件
applicationArguments);
Banner printedBanner = printBanner(environment);//打印启动提示字符,默认spring的字符图
context = createApplicationContext();//实例化一个可配置应用上下文
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,//3.准备上下文
printedBanner);
refreshContext(context);//4.刷新上下文
afterRefresh(context, applicationArguments);//5.刷新上下文后
listeners.finished(context, null);--关闭!
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}

如上图,容器启动流程可以分为5个主要步骤:

1.getRunListeners获取监听器(SpringApplicationRunListeners )

实际是SpringApplicationRunListener类

 private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
} private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
} private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 使用Set确保的字符串的唯一性
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 1.载入工厂名称集合
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,// 2.创建工厂实例
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);// 排序
return instances;
}

载入工厂名称(loadFactoryNames)

当前类的类加载器从META-INF/spring.factories文件中获取SpringApplicationRunListener类的配置

 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

上图,获取到工厂类名后,下面来看看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)生成"事件发布启动监听器"工厂实例

 @SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);// 利用反射获取类
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);// 得到构造器
T instance = (T) BeanUtils.instantiateClass(constructor, args);// 根据构造器和参数构造实例
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}

2.准备好环境

构造一个ConfigurableEnvironment,这里不多说。

3.准备上下文

 private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);//单例一个BeanNameGenerator,把ResourceLoader设置进应用上下文
applyInitializers(context);//执行初始化器
listeners.contextPrepared(context);// 监听器执行上下文"已准备好"方法
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
} // 添加spring boot特殊单例bean
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
} // 载入资源
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);// 监听器执行"上下文已加载"方法
}

4.刷新上下文

 private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);//核心类
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();//注册关闭钩子,容器关闭时执行
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
} protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}

最终执行的是AbstractApplicationContext抽象类的refresh方法。

 public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备刷新的上下文环境,例如对系统属性或者环境变量进行准备及验证。
prepareRefresh(); //启动子类的refreshBeanFactory方法.解析xml
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //为BeanFactory配置容器特性,例如类加载器、事件处理器等.
prepareBeanFactory(beanFactory); try {
//设置BeanFactory的后置处理. 空方法,留给子类拓展用。
postProcessBeanFactory(beanFactory); //调用BeanFactory的后处理器, 这些后处理器是在Bean定义中向容器注册的.
invokeBeanFactoryPostProcessors(beanFactory); //注册Bean的后处理器, 在Bean创建过程中调用.
registerBeanPostProcessors(beanFactory); //初始化上下文中的消息源,即不同语言的消息体进行国际化处理
initMessageSource(); //初始化ApplicationEventMulticaster bean,应用事件广播器
initApplicationEventMulticaster(); //初始化其它特殊的Bean, 空方法,留给子类拓展用。
onRefresh(); //检查并向容器注册监听器Bean
registerListeners(); //实例化所有剩余的(non-lazy-init) 单例Bean.
finishBeanFactoryInitialization(beanFactory); //发布容器事件, 结束refresh过程.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} //销毁已经创建的单例Bean, 以避免资源占用.
destroyBeans(); //取消refresh操作, 重置active标志.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
//重置Spring的核心缓存
resetCommonCaches();
}
}
}

5.刷新完上下文后

spring boot提供的2个供用户自己拓展的接口:ApplicationRunner和CommandLineRunner。可以在容器启动完毕后(上下文刷新后)执行,做一些类似数据初始化的操作。

 private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());//从上下文中获取ApplicationRunner类型的bean
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());//从上下文中获取CommandLineRunner类型的bean
AnnotationAwareOrderComparator.sort(runners);//排序
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);//执行
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
两个区别在于入参不同,根据实际情况自己选择。
 public interface CommandLineRunner {
void run(String... args) throws Exception;
} public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception; }

CommandLineRunner中执行参数是原始的java启动类main方法的String[] args字符串数组参数;ApplicationRunner中的参数经过处理提供一些方法例如:

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中就是:

.EventPublishingRunListener这个类封装了事件发布,

2.SpringApplicationEvent是spring-boot中定义的事件(上面说的6种事件),继承自ApplicationEvent(spring中定义的)

3.监听者 spring-boot并没有实现针对上述6种事件的监听者(我没找到...),这里用户可以自己实现监听者(上述6种事件)来注入spring boot容器启动流程,触发相应的事件。

例如:实现ApplicationListener<ApplicationReadyEvent>这个接口,在容器启动完毕时最后一步listener.finished时,如果启动没有异常,就会执行!可以做一些数据初始化之类的操作。

spring boot容器启动详解的更多相关文章

  1. Spring Boot 自定义日志详解

    本节内容基于 Spring Boot 2.0. 你所需具备的基础 什么是 Spring Boot? Spring Boot 核心配置文件详解 Spring Boot 开启的 2 种方式 Spring ...

  2. Spring Boot 集成 FreeMarker 详解案例(十五)

    一.Springboot 那些事 SpringBoot 很方便的集成 FreeMarker ,DAO 数据库操作层依旧用的是 Mybatis,本文将会一步一步到来如何集成 FreeMarker 以及配 ...

  3. Spring Boot 之 Redis详解

    Redis是目前业界使用最广泛的内存数据存储. Redis支持丰富的数据结构,同时支持数据持久化. Redis还提供一些类数据库的特性,比如事务,HA,主从库. REmote DIctionary S ...

  4. Spring Boot 之 HelloWorld详解

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “以前是人放狗看家,现在是狗牵着人散步” — 随笔 一.Spring Boot 自述 世界上最好 ...

  5. spring boot 面试题详解

    1.什么是springboot 用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文件) 创建独立的spring引用程序 main方法运行 嵌入的T ...

  6. Spring Boot Tomcat配置详解

    参数配置容器 server.xx开头的是所有servlet容器通用的配置,server.tomcat.xx开头的是tomcat特有的参数,其它类似. 所有参数绑定配置类:org.springframe ...

  7. Spring Boot的Maven插件Spring Boot Maven plugin详解

    Spring Boot的Maven插件(Spring Boot Maven plugin)能够以Maven的方式为应用提供Spring Boot的支持,即为Spring Boot应用提供了执行Mave ...

  8. Spring Boot Thymeleaf 使用详解

    在上篇文章Spring Boot (二):Web 综合开发中简单介绍了一下 Thymeleaf,这篇文章将更加全面详细的介绍 Thymeleaf 的使用.Thymeleaf 是新一代的模板引擎,在 S ...

  9. spring boot application.properties详解

    附上最新文档地址:https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-propertie ...

随机推荐

  1. 01.阿里云SDK调用,获取ESC主机详细信息

    一:通过python SDK获取云主机的详细信息 1.创建Accessky码(不做展示) 2.通过pip安装SDK模块,这个阿里云帮助里面有,也不做详细展示. 3.详细使用方法看代码 我下面展示的返回 ...

  2. Linux下磁盘监控及系统版本-CPU-内存等查看

    1.磁盘IO监控工具 iotop 输入命令:iotop   主要查看程序使用的磁盘IO的信息 安装:yum -y install iotop 第一行:10:01:23 — 当前系统时间126 days ...

  3. maven 打包Could not resolve dependencies for project和无效的目标发行版: 1.8

    1.maven 打包Could not resolve dependencies for project 最近项目上使用的是idea ide的多模块话,需要模块之间的依赖,比如说系统管理模块依赖授权模 ...

  4. golang 轮训加密算法

    Roy's friends has been spying on his text messages, so Roy thought of an algorithm to encrypt text m ...

  5. Django入门实战【3步曲】

      环境准备 junhongdeMacBook-Air:site-packages junhongchen$ python -V Python 2.7.10 junhongdeMacBook-Air: ...

  6. MicroPython教程之TPYBoard开发板DIY小型家庭气象站

    众所周知,iPhone6/6Plus内置气压传感器,不过大家对于气压传感器还是很陌生.跟字面的意思一样,气压传感器就是用来测量气压的,但测量气压对于普通的手机用户来说又有什么作用呢? 海拔高度测量 对 ...

  7. git和github新手安装使用教程(三步入门)

    git和github新手安装使用教程(三步入门) 对于新手来说,每次更换设备时,github的安装和配置都会耗费大量时间.主要原因是每次安装时都只关心了[怎么做],而忘记了记住[为什么].本文从操作的 ...

  8. Linux 创建子进程执行任务

    Linux 操作系统紧紧依赖进程创建来满足用户的需求.例如,只要用户输入一条命令,shell 进程就创建一个新进程,新进程运行 shell 的另一个拷贝并执行用户输入的命令.Linux 系统中通过 f ...

  9. 使用Tomcat的Reload提高开发速度(翻译)

    欢迎转载http://www.cnblogs.com/coodream2009,有翻译的不太准确的地方请大家指出,我继续修改完善. 按照Java Servlet规范第四部分推荐的,Tomcat系统的实 ...

  10. blog建表操作

    表思维导图:   数据库:表 from django.db import modelsfrom django.conf import settingsfrom django.contrib.auth. ...