本文泛式讲解springboot的启动过程。
springboot的启动方式
方式一:
一个简单的springBoot项目,大致格式如下:
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
SpringApplication这个类是可以设置属性的。
方式二:
修改后的启动方式:
@SpringBootApplication
public class DemowebApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication();
application.setAdditionalProfiles("prod");//设置profile
application.setWebApplicationType(WebApplicationType.SERVLET);//设置项目类型为web项目
HashSet<String> source = new HashSet<>();
//此处可以设置多个类名,便于启动之前写好的组件或者插件
//此处的类名不强求是当前类,但是这里设置的每个类上面都必须要有@SpringBootApplication注解
source.add(DemowebApplication.class.getName());
application.setSources(source);
application.run(args);
}
}
springboot的项目,有以下应用类型:
SERVLET类型:平时的交互式springboot项目。
REACTIVE类型:响应式springboot项目,比如webflux。
NONE类型的:无容器项目,单纯作为jar包的。
关于流式springboot项目,webflux,支持一个请求,然后每隔一段时间(比如每秒)向前端输出内容,但是也需要前端进行部分支持。
方式三:
下面的启动方式,以流式启动方式:
@SpringBootApplication
public class DemowebApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.profiles("prod")
.web(WebApplicationType.SERVLET)
.sources(DemowebApplication.class)
.run(args);
}
}
其中,source如果是类,可以直接设置在SpringApplication
或SpringApplicationBuilder
类的构造方法中。但如果source如果是xml,那么只能用方式二的那一种了。
springboot启动内部
springboot的启动分为两部分:一部分是上面的注解,我们叫做注解部分,一部分是SpringApplication.run(…)方法,我们叫做run部分。
注解部分
springboot的启动,是从@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 {
//...
}
这是一个标记类,就和@Service标明这是一个Service一样。同时,他也是触发开启自动配置和扫描的入口。
在@SpringBootApplication
注解上,有@SpringBootConfiguration
和@EnableAutoConfiguration
两个重要的注解。前者是标记springboot自动化配置的。后者是加载资源和类文件的。
关于前者的使用在org.springframework.boot.test.context.SpringBootConfigurationFinder
类的构造器中:
SpringBootConfigurationFinder() {
this.scanner = new ClassPathScanningCandidateComponentProvider(false);
this.scanner.addIncludeFilter(
new AnnotationTypeFilter(SpringBootConfiguration.class));
this.scanner.setResourcePattern("*.class");
}
如果扫描这个类,那么配置文件会添加入class文件,然后会去找@Configuration的类了。
关于后者,代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//...
}
其中@AutoConfigurationPackage这个注解,标明包含这个注解的包,都会被注册进去。所以,一般的项目启动文件XXXApplication.java在外面,而其他类会被再包裹一次package。
在其注解上有@Import(AutoConfigurationImportSelector.class)
注解,会加载Servlet、Bean、Resource、BeanFactory、Env环境。下面可以看到这些基本都是这个类的父接口。
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
//...
}
在其继承的接口中,而Ordered是用来排序的,等价于注解@Order,是绝对顺序。而相对顺序的有@Conditional系列注解的。关于@Conditional系列注解的使用,在别处一话。
DispatcherServlet的初始化
众所周知,在springmvc中,由dispatcherServlet来进行请求分发。而在springboot中,DispatcherServlet的注册,依赖于org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
,在其构造方法中,初始化了DispatcherServlet:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
return dispatcherServlet;
}
在下面的dispatcherServletRegistration方法中,予以注册:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
dispatcherServlet,
this.serverProperties.getServlet().getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
Web自动化配置
Web自动化配置在org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
类中。这也是一个@Configuration类。该类中有众多重要的子类。
其内部有静态内部类org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
:
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapterimplements WebMvcConfigurer {
//...
}
其中,父接口WebMvcConfigurer
是用来实现java-config的。
导入类org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
类,相当于@EnableWebMvc
注解:
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
//...
}
run部分
public ConfigurableApplicationContext run(String... args) {
//StopWatch是一个计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//强制设置,没有窗口也可以启动
configureHeadlessProperty();
//设置日志,监听
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//打印Spring字母的图画,如果定制,可以重写这个
Banner printedBanner = printBanner(environment);
//创建webApplicationContext
context = createApplicationContext();
//生成错误报告器类
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//预备上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//该方法最终执行的是org.springframework.context.support.AbstractApplicationContext#refresh方法,目的是配置可以加入后面的子类及其工厂,注册bean,国际化,以及事件监听器等信息
refreshContext(context);
//该方法留以扩展
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
//在项目启动后执行所有的Runner
//如果有什么事情是需要在项目启动后要做的,就继承ApplicationRunner或者CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//监听context
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
在org.springframework.boot.SpringApplication
中,会判断项目的类型,该方法会在SpringApplication构造方法中最后一层的构造方法SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
中进行调用:
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
预备上下文方法:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境参数
context.setEnvironment(environment);
//注册单例beanNameGenerator(beanNameGenerator是用来生成扫描Bean的名字到容器中)
postProcessApplicationContext(context);
//对于已经创建但为初始化的Initializer进行初始化
applyInitializers(context);
//所有监听器预备监听
listeners.contextPrepared(context);
if (this.logStartupInfo) {
//调用log打印日志
logStartupInfo(context.getParent() == null);
// 输出当前的profile,默认为default
logStartupProfileInfo(context);
}
// 注册springboot指定的单例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//设置bean加载器,加载bean
load(context, sources.toArray(new Object[0]));
//监听器监听上下文
listeners.contextLoaded(context);
}
补充:
在底层看的时候有很多自动装配类,对于自动装配的相对顺序与绝对顺序注解:
AutoConfigureOrder:绝对顺序(指定顺序的数值)
AutoConfigureAfter:相对顺序后(指定类是在哪些类之后)
AutoConfigureBefore:相对顺序前
可以一起共用,辅助看代码。
本文作于2019年9月,隐于简书。
今日示于公众号。




