spring-boot-mvc启动流程
1、前言
在web应用开发中,我们比较常用的是spring boot+spring mvc的组合。该组合的核心点就是内嵌tomcat和mvc的dispatcherServlet,今天,我们就来探究一下其启动流程。
前置知识点:spring boot原理、spring mvc原理
源码版本:spring boot 2.1.7
2、流程
此处先给出结论,抓住主线,便于后面的学习。
核心步骤分四步:
- 容器类型确定
- 创建web容器
- 启动内嵌tomcat
- 启动DispatcherServlet
大致时序图如下:
3、容器类型
容器类型,由WebApplicationType枚举类定义:
- web容器
- mvc容器
- reactive容器
- 非web容器
SpringApplication的构造方法中设置
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
。。。。
//调用方法设置容器类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
。。。。
this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath()获取容器类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
//reactive
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
//普通容器
return WebApplicationType.NONE;
}
}
//返回servlet容器,即mvc
return WebApplicationType.SERVLET;
}
- WebApplicationType.REACTIVE - 当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时
- WebApplicationType.NONE - 也就是非Web型应用(Standard型),此时类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时
- WebApplicationType.SERVLET - 类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型时
4、创建web容器
在上步,我们已经确定了容器的类型,接下来就是创建容器了。核心步骤:
- 根据类型获取定义的Class
- 通过反射创建容器
源码如下:
**SpringApplication.run()方法中的createApplicationContext()**执行
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境,确认activefile,确认配置的文件的内容全部可用,此处会准备bootstarpapplication,
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
. ...
//创建容器
context = createApplicationContext();
...
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器,关键点,tomcat在这里面的onrefresh中创建
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
...
return context;
}
createApplicationContext()方法。根据容器类型创建容器,mvc的容器为ServletWebServerApplicationContext。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
//根据容器type加载class
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
//需要的servletWeb容器的Class,mvc
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
...
}
}
//根据class创建并返回web容器
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
5、 启动tomcat
我们观察项目启动的日志,会有tomcat的相关日志。debug找到相关的类,便于确定调用堆栈。
Debug的调用堆栈如下:
我们跟着调用堆栈走,先看看onRefrensh()方法。
5.1、onRefresh
ServletWebServerApplicationContext实现了onRefresh的方法,该方法在AbstractApplicationContext无实现,交由子类扩展。
实现逻辑如下:
protected void onRefresh() {
super.onRefresh();
try {
//创建webServer
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
5.2、createWebServer
该方法中通过工厂获取webServer,根据不同的类型,servlet容器是tomcat。
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//通过工厂获取,先加载工厂,此处是tomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
//工厂生产产品,tomcatServer
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();
}
5.2、 getWebServerFactory
然后通过容器getBean获取工厂bean,如果还没有,就会创建,进入getBean---->createBean的逻辑。在获取tomcatServletWebServerFactory中,引入了DispatcherServlet
protected ServletWebServerFactory getWebServerFactory() {
//名字是tomcatServletWebServerFactory
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
....
//先获取工厂,然后通过getBean获取工厂bean,此处是tomcatServletWebServerFactory
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
5.2.1 tomcatServletWebServerFactory注入
上图,获取tomcatServletWebServerFactory工厂,肯定需要该工厂的bean定义,那么定义在哪呢?
是在ServletWebServerFactoryConfiguration.EmbeddedTomcat中注入的,源码如下:
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
//配置TomcatServletWebServerFactory的bean,用于创建tomcatServer
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
......
return factory;
}
}
}
5.2.2、ServletWebServerFactoryConfiguration.EmbeddedTomcat
ServletWebServerFactoryConfiguration.EmbeddedTomcat是由ServletWebServerFactoryAutoConfiguration注入的。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//是在auto-configuation中注入的该类
@EnableConfigurationProperties(ServerProperties.class)
//注入了tomcat的工厂
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
//tomcat相关
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
//tomcat的自定义工厂,
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
//注册后置处理器
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
//bean的后置处理器,errorPageRegistrarBeanPostProcessor最后挂到了DispatchServlet上
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
}
}
ServletWebServerFactoryAutoConfiguration是在auto-configuration的配置文件中注入的。
可以看见图片中,大多servlet相关的自动注入都在此处。
5.2.3、errorPageRegistrarBeanPostProcessor
创建tomcatServletWebServerFactory后对其进行后置处理时,用到了errorPageRegistrarBeanPostProcessor,该后置处理器初始化时最后调用了DispatchServlet
errorPageRegistrarBeanPostProcessor会获取register
private Collection<ErrorPageRegistrar> getRegistrars() {
if (this.registrars == null) {
// Look up does not include the parent context
this.registrars = new ArrayList<>(
//引入了ErrorPageRegistrar.class,关键点
this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());
this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);
this.registrars = Collections.unmodifiableList(this.registrars);
}
return this.registrars;
}
5.2.4、ErrorPageRegistrar
唯一实现ErrorPageCustomizer,构造方法需要DispatcherServletPath的类
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
private final ServerProperties properties;
//DispatcherServletPath
private final DispatcherServletPath dispatcherServletPath;
//引入DispatcherServletPath,关键点
protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
this.properties = properties;
this.dispatcherServletPath = dispatcherServletPath;
}
}
3.2.5、DispatcherServletPath
DispatcherServletRegistrationBean是DispatcherServletPath的实现,构造方法引入了DispatcherServlet
/**
* ServletRegistrationBean for the auto-configured DispatcherServlet. Both registers the servlet and exposes * DispatcherServletPath information.
*/
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet>
implements DispatcherServletPath {
private final String path;
//DispatcherServlet,关键类
public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
super(servlet);
Assert.notNull(path, "Path must not be null");
this.path = path;
super.addUrlMappings(getServletUrlMapping());
}
}
5.2.6 DispatcherServlet
DispatcherServlet的由DispatcherServletAutoConfiguration注入如下:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
//关键
//注入了dispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
......
return dispatcherServlet;
}
}
}
DispatcherServletAutoConfiguration也是由spring.factories的auto-configura注入
5.3、getWebServer
获取webServer,在该方法中,tomcat出场了。
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//tomcat出场
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);
//service
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
//engine
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
//创建tomcatwebserver
return getTomcatWebServer(tomcat);
}
5.3.1 getTomcatWebServer
获取TomcatWebServer,原生的tomcat启动有些类似。
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
//初始化
initialize();
}
//初始化tomcat
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
//熟悉的地方
// Start the server to trigger initialization listeners
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
6、 初始化DispatcherServlet
tomcatServer启动时还没有初始化DispatcherServlet,9大组件都未初始化,等到有请求进来了,再去初始化DispatcherServlet。DispatcherServlet的初始化在onRefresh中:
//刷新
protected void onRefresh(ApplicationContext context) {
//初始化
initStrategies(context);
}
//初始化
protected void initStrategies(ApplicationContext context) {
//9大组件
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
6.1、onRefrsh
onRefresh由父类FrameworkServlet的initWebApplicationContext()调用,初始化逻辑此处不介绍。
protected WebApplicationContext initWebApplicationContext() {
//web容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
....
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
//关键刷新
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
6.2、调用栈
具体的调用堆栈如下:
由下至上可以看出:
- NioEndpoint接受到请求,交由Processor处理。
- 不同的协议是不同的Processor处理。此处是Http11Processor,处理请求,封装Request对象,然后交由Engine引擎
- Engine解析Host
- Host确定容器
- 容器确定Wrappper,Wrapper就是Servlet的包装。此处就是DispatchServlet
- 然后DispatchServlet初始化并分发请求。