springboot启动流程
本篇博客也是由于自己在学习springboot时候发现其启动类封装的如此简洁,而功能却又十分庞大,顾查阅各种资料和大佬博客,经过自己整理从而完成该内容。
1.顶层注解@SpringBootApplication
1.1具体实现
@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 {
@AliasFor(
其注解主要目的是开启自动配置去加载配置文件中对应的配置,当我进入@EnableAutoConfiguration注解类可以看出
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以看到有个@Import注解,当我们进入该类中就可以看出此类便是我们要找的自动加载配置类的核心代码了。通常被@Import注解导入的类的#selector方法会在spring的生命周期的invokeBeanFactoryPostProcessors阶段进行调用,我们接着来看selector方法。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
分析
(1).通过#loadMetadata方法去加载自动过滤配置文件中的类,该配置文件一般
META-INF/spring-autoconfigure-metadata.properties路径下
截取配置中前谢行如下:
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration.ConditionalOnWebApplication=REACTIVE
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=REACTIVE
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration=
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.ConditionalOnClass=com.couchbase.client.java.Cluster,com.couchbase.client.java.CouchbaseBucket
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplate
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureOrder=-2147483648
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration=
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureOrder=-2147483638
配置了一些自动过滤的类
(2).通过调用#getAutoConfigurationEntry并利用spI机制加载META-INF/spring.factories中所有配置类
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//获取注解中对应的值
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//加载spring.factory文件中所有类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根据自动装配过滤配置文件spring-autoconfigure-metadata.properties中配置的规则去过滤一些的类
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
当debug到#getCandidateConfigurations时可以看出类已经全部加载完毕
可以看出filter中主要是根据spring-autoconfigure-metadata.properties
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
//表示是否跳过当前候选类
boolean skipped = false;
Iterator var8 = this.getAutoConfigurationImportFilters().iterator();
while(var8.hasNext()) {
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
this.invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for(int i = 0; i < match.length; ++i) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;//如果候选类为true则会跳过
}
}
}
if (!skipped) {
return configurations;
} else {
List<String> result = new ArrayList(candidates.length);
int numberFiltered;
for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
if (!skip[numberFiltered]) {
result.add(candidates[numberFiltered]);
}
}
if (logger.isTraceEnabled()) {
numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList(result);
}
}
当我们点击进入spring.factory其中一个配置类时
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnMissingBean({RepositoryRestMvcConfiguration.class})
@ConditionalOnClass({RepositoryRestMvcConfiguration.class})
@AutoConfigureAfter({HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class})
@EnableConfigurationProperties({RepositoryRestProperties.class})
@Import({RepositoryRestMvcConfiguration.class})
public class RepositoryRestMvcAutoConfiguration {
public RepositoryRestMvcAutoConfiguration() {
}
@Bean
public SpringBootRepositoryRestConfigurer springBootRepositoryRestConfigurer() {
return new SpringBootRepositoryRestConfigurer();
}
}
可以看出其包含几种注解而这些注解的含义如下:
@Conditional(T.class)
这句代码可以标注在类上面,表示该类下面的所有@Bean都会启用配置,也可以标注在方法上面,只是对该方法启用配置。
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)
@AutoConfigureOrder 自动配置加载顺序
@AutoConfigureBefore 在制动配置加载之后加载该类
@AutoConfigureAfter 在制动配置加载之前加载该类
扩展
如果需自己添加配置类可以在自己工程的resouses目录下建立META-INF/spring.factories文件将自己的的配置类加进去
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
x.xx.xxAutoConfiguration,\
x.xxx.xxxAutoConfiguration
2.启动#run
2.1具体实现
点进run方法里
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
可以看出此时回去创建一个SpringApplication实例并且之后会去调用其run方法
在new SpringApplication实例时代码如下
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//(1)
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//(2)
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
其主要工作就两步
(1)通过加载spring.factories文件里的key为ApplicationContextInitializer的类进行实例化
(2)通过加载spring.factories文件里的ApplicationListener.class对应类进行实例化
下面是具体的run方法
代码如下:
public ConfigurableApplicationContext run(String... args) {
//创建计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//获取spring.facotries下的所有监听类并执行其starting方法
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//运行starting方法
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//分析(1)
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = this.printBanner(environment);
//创建上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
分析(2)
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
分析(3)
this.refreshContext(context);
//刷新完成
this.afterRefresh(context, applicationArguments);
//停止计时器
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
分析(1)#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//创建配置环境变量根据不同类型创建不同环境配置类StandardServletEnvironment()
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//配置环境具体信息
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
//调用监听类中的环境准备方法
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
this.configurePropertySources(environment, args);
//根据指定的配置spring.profiles.active属性指定具体的application.yml
this.configureProfiles(environment, args);
}
分析(2)prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境变量
context.setEnvironment(environment);
//设置setResourceLoader和setClassLoader以及setConversionService
this.postProcessApplicationContext(context);
//循环初始化ApplicationContextInitializer实现类
this.applyInitializers(context);
//调用监听类中contextPrepared方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
//加载Soures
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
分析(3)refreshContext
该方法为启动的核心代码包括内置的Tomcat容器启动和初始化也在该方法中完成
具体代码如下:
private void refreshContext(ConfigurableApplicationContext context) {
//核心流程
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//准备刷新上下文环境
this.prepareRefresh();
//刷新子类中的benafactory工厂
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//准备bean工程为上下文使用
this.prepareBeanFactory(beanFactory);
try {
//允许在上下文子类中对bean工厂进行后处理。
this.postProcessBeanFactory(beanFactory);
//激活bean工厂中的处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截bean创建的bean处理器
this.registerBeanPostProcessors(beanFactory);
//初始化消息体对不同语言的消息体进行国际化处理
this.initMessageSource();
//为此上下文初始化事件广播器。
this.initApplicationEventMulticaster();
//让子类来初始化其他bean
this.onRefresh();
//在注册的bean中查找监听bean,注册到消息广播中去
this.registerListeners();
//完成所有的beanfactory初始化
this.finishBeanFactoryInitialization(beanFactory);
//完成整个刷新过程,并initLifecycleProcessor和publishEvent
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
分析:
整个流程如下
- 准备刷新上下文环境
- 刷新子类中的benafactory工厂
- 准备bean工程为上下文使用
- 允许在上下文子类中对bean工厂进行后处理
- 激活bean工厂中的处理器
- 注册拦截bean创建的bean处理器
- 初始化消息体对不同语言的消息体进行国际化处理
- 为此上下文初始化事件广播器。
- 让子类来初始化其他bean
- 在注册的bean中查找监听bean,注册到消息广播中去
- 完成所有的beanfactory初始化
- 完成整个刷新过程,并initLifecycleProcessor和publishEvent
而其中在onRefresh中进行了webserver的创建及在该步骤中
进入ServletWebServerApplicationContext中可以看到
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
进入createWebServer方法中
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = this.getWebServerFactory();
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
在getWebServer中实现了如下几种方式也是目前springboot支持的三种容器一是Tomcat 二是Jetty 三是Undertow
这三种容器目前用的最多的是Tomcat和Jetty,但Undertow也渐渐流行起来,据说在高并发下undertow的性能更好。
点击Tomcat的getServer可以看到Tomcat容器的加载过程
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
至此整个springboot的启动过程已全部完成总结如下:
1.通过顶层注解@SpringBootApplication自动加载META-INF下的配置spring-autoconfigure-metadata.properties和spring.factories两个配置文件
2.然后通过SpringApplication调用run方法,先是创建自身类在构造方法中加载并设置spring.factories文件中的ApplicationContextInitializer和ApplicationListener
3.之后调用run方法完成整个启动过程,run方法的具体步骤如下:
- 创建计时器
- 获取spring.facotries下的所有监听类并执行其starting方法
- 运行starting方法
- 根据环境变量中配置的信息加载具体配置文件
- 打印Banner
- 创建上下文ApplicationContext
- 准备上下文ApplicationContext
- 刷新上线文ApplicationContext
- 刷新完成
- 停止计时器