上一篇文章中(三分钟手写极简版SpringBoot-重复造个轮子)我们写了一个极简版的SpringBoot,基本完成了Controller的映射和访问,真实的SpringBoot是如何来做的?我们以2.3.0.RELEASE版本的SpringBoot为例来看下它是如何一步一步创建内嵌的tomcat并且注册DispatcherServlet的,它是否跟上篇文章中讲的一样,也是利用了Servlet3.0的ServletContainerInitializer和WebApplicationInitializer呢?
首先从SpringApplication#run方法开始:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
继续往下走,一直到:
public ConfigurableApplicationContext run(String... args) {
…
try {
…
//这里会创建AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
//看下这里
refreshContext(context);
…
}
…
return context;
}
private void refreshContext(ConfigurableApplicationContext context) {
//重点看这里
refresh(context);
if (this.registerShutdownHook) {
...
}
}
SpringApplication#refresh:
protected void refresh(ConfigurableApplicationContext applicationContext) {
//这个就是前面创建出来的AnnotationConfigServletWebServerApplicationContext
applicationContext.refresh();
}
这个refresh就是Spring核心的那个refresh方法。AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,因此,会调用到ServletWebServerApplicationContext#refresh:
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
通过上面的代码也能很容易的看出来,里面肯定创建了webServer,默认就是tomcat,它的refresh实际上啥也没干,直接是调用的父类的refresh,也就是AbstractApplicationContext#refresh,熟悉spring的小伙伴对这个方法应该非常熟悉,这个方法就是用来初始化整个Spring的环境的,做的事情很多,我们就不再做太多介绍,里面会执行到一行onRefresh(),因为ServletWebServerApplicationContext重写了onRefresh():
@Override
protected void onRefresh() {
super.onRefresh();
try {
//就是在这里创建的webserver
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
ServletWebServerApplicationContext#createWebServer():
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
//这里真正去创建tomcat
this.webServer = factory.getWebServer(getSelfInitializer());
。。。
}
。。。
}
这里首先是调用了getSelfInitializer(),然后把拿到的ServletContextInitializer传递给getWebServer()方法,注意下ServletContextInitializer,SpringBoot就是用这个接口来完成类似Servlet3.0的ServletContainerInitializer的功能的,进去到getWebServer()里面,TomcatServletWebServerFactory#getWebServer():
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);
//添加了connector
tomcat.getService().addConnector(connector);
//在这里设置了connector的端口号
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
//这里会把ServletContextInitializer传进去
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
看下TomcatServletWebServerFactory#prepareContext():
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
…
//这里又添加了2个ServletContextInitializer
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
//重点看下这里
configureContext(context, initializersToUse);
postProcessContext(context);
}
TomcatServletWebServerFactory#configureContext():
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
//这里创建了一个TomcatStarter,传递进去ServletContextInitializer
TomcatStarter starter = new TomcatStarter(initializers);
//TomcatEmbeddedContext继承了tomcat里面的StandardContext
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
//把TomcatStarter添加到StandardContext的一个成员变量starter中
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
//把TomcatStarter添加到StandardContext的initializers中,
//initializers是一个Map
context.addServletContainerInitializer(starter, NO_CLASSES);
。。。
}
TomcatStarter是一个非常关键的类,它实现了ServletContainerInitializer接口,这个接口是servlet提供的,因此TomcatStarter是连接SpringBoot和tomcat的一个关键纽带。
继续往下执行就到了TomcatServletWebServerFactory#getTomcatWebServer():
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
….
//这里会去启动tomcat
initialize();
}
TomcatWebServer#initialize():
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
。。。
// 启动tomcat
this.tomcat.start();
//启动一个阻塞线程,防止tomcat退出
startDaemonAwaitThread();
。。。
}
Tomcat启动以后,会执行到tomcat里面的StandardContext#startInternal():
protected synchronized void startInternal() throws LifecycleException {
...
// initializers是前面存进去的,里面只有一个TomcatStarter
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
//这里会调用TomcatStarter的onStartup()
//TomcatStarter里面有所有的ServletContextInitializer,因此这里就会回调到SpringBoot里面的ServletContextInitializer里面去
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
...
}
TomcatStarter#onStartup():
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
// initializers也是前面存进去的,里面一共3个,其中一个是ServletWebServerApplicationContext里面的selfInitialize
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
...
}
}
ServletWebServerApplicationContext#selfInitialize():
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//这里面就拿到了DispatcherServletRegistrationBean
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
首先看下ServletWebServerApplicationContext#getServletContextInitializerBeans():
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
//这里去加载DispatcherServletRegistrationBean
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,initializerType)) {
//这里就有DispatcherServletRegistrationBean
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
拿到了DispatcherServletRegistrationBean,上面继续执行它的onStartup(),RegistrationBean#onStartup():
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
//注册DispatcherServlet
register(description, servletContext);
}
DynamicRegistrationBean# register():
@Override
protected final void register(String description, ServletContext servletContext) {
//注册DispatcherServlet
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
//设置映射路径和启动级别
configure(registration);
}
ServletRegistrationBean#configure():
@Override
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
//设置映射路径
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
//设置启动级别
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}
至此,整个Spring环境和DispatcherServlet就都加载起来了。
现在还剩下一个问题,DispatcherServletRegistrationBean是如何加载到Spring的容器的呢?
在spring-boot-autoconfigure-**.jar的META-INF下面的spring.factories里面配置了一个DispatcherServletAutoConfiguration,在这里面去创建了DispatcherServletRegistrationBean。
整理下整个启动过程:
(1)SpringBoot首先扫描classpath,根据里面是否有web相关的类去创建了AnnotationConfigServletWebServerApplicationContext。
(2)AnnotationConfigServletWebServerApplicationContext在它的refresh()里面回调了ServletWebServerApplicationContext的onRefresh(),在这里面去创建了tomcat。
(3)SpringBoot把实现了ServletContextInitializer接口的实现类传递到了TomcatStarter里面,TomcatStarter是由SpringBoot提供的,它同时实现了ServletContainerInitializer接口,而ServletContainerInitializer是Servlet提供的,因此,TomcatStarter是一个非常关键的纽带。
(4)当tomcat启动以后,内部会调用所有的ServletContainerInitializer的onStartup(),因此就回调到了TomcatStarter的onStartup(),进而回调到了ServletContextInitializer里面。
(5)在ServletWebServerApplicationContext里面的一个ServletContextInitializer里面去加载了DispatcherServletRegistrationBean,在它的onStartup()回调里面去创建了DispatcherServlet,并且设置了映射路径和启动级别。
(6)整个启动流程可以看出来,SpringBoot并不是像SpringMVC那样通过回调ServletContainerInitializer来完成加载。SpringBoot是直接生成了它的一个叫TomcatStarter的子类,然后在其中把对ServletContainerInitializer的回调转移到对ServletContextInitializer的回调,然后在ServletContextInitializer中去加载的DispatcherServlet。
欢迎扫码查看更多文章:
![4afdf0107cb7ff799b5cc3df5a60df1f.png](https://i-blog.csdnimg.cn/blog_migrate/3a0946165f7231a12fd554ad4159ef7d.png)