用过SpringBoot的都知道,如下代码就是一个最基本的SpringBoot主启动类,通过启动这个类,即可完成SpringBoot的启动装配等功能。
@SpringBootApplication
public class SpringBootMainApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMainApplication.class, args);
}
}
所以,来看看,到底是如何实现的?
首先,可以看到,只有两处特殊的地方
- @SpringBootApplication
- SpringApplication.run(SpringBootMainApplication.class, args);
一、@SpringBootApplication
首先,可以看到
第一个注解 @ComponentScan,进行包扫描(都知道,放在主启动类所在包,以及子包下的组件,都会被扫描进入容器中)
第二个注解 @EnableAutoConfiguration, 看名字就知道,启动启动装配,通过AOP/事务的套路之后,都知道@EnableXXX,会导入一些组件,进而实现一些功能
@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 {}
所以,我们进入@EnableAutoConfiguration中看看,一看,果然如此,导入了一个ImportSelector,在ioc容器启动的时候,会调用其selectImports方法,往容器中添加组件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
在 getAutoConfigurationEntry 中,进行关键的操作,
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 在这,找寻候选的配置,实际上,读取resources文件夹下的spring.factories文件,进行自动装配
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
// getCandidateConfigurations,寻找的是,spring.factories中的 EnableAutoConfiguration配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
//
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
然后,执行完这个之后,自动装配就算完成了,会去读取所有导入的jar包中的spring.factories文件中的EnableAutoConfiguration
二、SpringApplication.run(SpringBootMainApplication.class, args);
run方法有多个重载,最终调用到的run方法如下,分为两步
- 创建SpringApplication对象
执行SpringApplication对象中的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
new SpringApplication(primarySources)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 设置资源加载器,默认为null,不管
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 把主启动类保存到SpringApplication对象中
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断当前容器的环境状态,看是否是web环境/普通的Java环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加载spring.factories中的 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载spring.facotries中的 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 找到main方法所在类,没有的话,返回null
this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {
// 判断是否有webflux环境,如果是,返回reactive模式
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// SERVLET_INDICATOR_CLASSES中包含 Servlet/ConfigurableWebApplicationContext两个元素
// 如果但凡导入了一个不存在,说明不是servlet环境,那就是一个普通Java环境
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 否则,就是一个web环境,需要创建web版的spring容器
return WebApplicationType.SERVLET;
}
deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
run(args)
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 获取spring.factories中配置的 SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 预准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印logo
Banner printedBanner = printBanner(environment);
// 创建ioc容器
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文
refreshContext(context);
// 刷新上下文之后的处理工作
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布一个事件,ioc容器启动完成
listeners.started(context);
// 调用Runner的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 发布一个事件,容器已经正在运行
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
prepareEnvironment(listeners, applicationArguments); 预准备环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 发布容器准备完成的事件
listeners.environmentPrepared(environment);
// 把environment绑定到SpringApplication对象中
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据当前的环境,判断创建哪个ApplicationContext
switch (this.webApplicationType) {
case SERVLET:
// mvc AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
// webflux AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
// 默认普通Java AnnotationConfigApplicationContext
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置 environment
context.setEnvironment(environment);
// 对容器进行后置处理,注册 beanNameGenerator/resourceLoader等
postProcessApplicationContext(context);
// 调用所有的ApplicationContextInitializer的initialize方法,进行初始化
applyInitializers(context);
// 发布容器准备完成事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
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());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 发布容器加载完成事件
listeners.contextLoaded(context);
}
refreshContext(context)
在这调用ioc的刷新方法,执行refresh的12步,其中有一步为onRefresh方法,在mvc环境中,重写了onRefresh方法
private void refreshContext(ConfigurableApplicationContext context) {
// 调用ApplicationContext的refresh方法,ioc刷新方法
refresh(context);
if (this.registerShutdownHook) {
try {
// 注册hook
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
在这,createWebServer,创建tomcat
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
// createWebServer
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
//调用Servelet工厂获取WebServer服务
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
// 以tomcat为例的话,就创建出一个tomcat对象,
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
// 在这,就执行初始化,监听端口等
return getTomcatWebServer(tomcat);
}
SpringBoot的大致启动流程至此结束