一、前言

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. Vue入门总结

    技术栈:VUE:Vue-router:Vue-resource:Vue-cli: 项目:个人博客vue重构 一.vue-cli脚手架搭建项目结构 全局安装vue-cli: npm install vu ...

  2. 获取数据库时间sql 以及行级锁总结-共享锁-排他锁-死锁

    --TRUNC(date,[fmt]) /TRUNC(number[,decimals])SELECT SYSDATE FROM dual;SELECT TRUNC(SYSDATE) FROM dua ...

  3. Android studio Error:org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection cannot be cast to

    http://blog.csdn.net/FlyRabbit_1/article/details/74536317 Error:org.gradle.api.internal.tasks.Defaul ...

  4. ssh的action校验内容输出

    当form里为input类型时,如<input type="text" name="manager.name" />,则在对应的jsp中要使用< ...

  5. MVC框架实例构建

    转自:http://www.cnblogs.com/levenyes/p/3290885.html MVC全名是Model View Controller,是模型(model)-视图(view)-控制 ...

  6. jQuery 实现无限任意添加下拉菜单

    新学jQuery还有很多没学,今天做了个下拉菜单,按照自己的思想结合学的基础效果实现一款可以任意添加层数的下拉菜单,如果有什么建议,欢迎指教啦啦啦 我喜欢备注细一些,这样给自己也是一种理解和方便回顾哈 ...

  7. mysql加锁读

    Locking Reads 在同一个事务中,如果你先查询数据,随后对相关数据进行插入或修改,那么在标准的SLELECT中不会给出足够的保护.在你查询期间另一个事务可以更新或者删除相同的行.InnoDB ...

  8. linux上配置bochs,搭建基于X86架构操作系统的开发环境

    学习操作系统最好的方法就是自己编写新的操作系统,或者修改已有的操作系统.但是如果在真机上完成这个过程,调试会成为一个很大的问题.利用虚拟机来完成,可以使调试过程变得简单,而且能节约很多开关机的时间. ...

  9. 【转载】MySQL之权限管理

    一.MySQL权限简介 关于mysql的权限简单的理解就是mysql允许你做你全力以内的事情,不可以越界.比如只允许你执行select操作,那么你就不能执行update操作.只允许你从某台机器上连接m ...

  10. 大话python面向对象

    前言 师门中每逢成人礼就要下山历练一番,小掌门今年成人礼,下山也有一段时日.可恰逢年底,今年百姓收成都不是很好,各大店铺也很少招人,再加上学艺不精,小掌门无事可做就只能饿肚子了.后来小掌门饿的实在不行 ...