上一篇文章中(三分钟手写极简版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 {…//这里会创建AnnotationConfigServletWebServerApplicationContextcontext = createApplicationContext();//看下这里refreshContext(context);…}…return context;}private void refreshContext(ConfigurableApplicationContext context) {//重点看这里refresh(context);if (this.registerShutdownHook) {...}}
SpringApplication#refresh:
protected void refresh(ConfigurableApplicationContext applicationContext) {//这个就是前面创建出来的AnnotationConfigServletWebServerApplicationContextapplicationContext.refresh();}
这个refresh就是Spring核心的那个refresh方法。AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,因此,会调用到ServletWebServerApplicationContext#refresh:
@Overridepublic 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():
@Overrideprotected void onRefresh() {super.onRefresh();try {//就是在这里创建的webservercreateWebServer();}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();//这里真正去创建tomcatthis.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);//添加了connectortomcat.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个ServletContextInitializerServletContextInitializer[] initializersToUse = mergeInitializers(initializers);host.addChild(context);//重点看下这里configureContext(context, initializersToUse);postProcessContext(context);}
TomcatServletWebServerFactory#configureContext():
protected void configureContext(Context context, ServletContextInitializer[] initializers) {//这里创建了一个TomcatStarter,传递进去ServletContextInitializerTomcatStarter starter = new TomcatStarter(initializers);//TomcatEmbeddedContext继承了tomcat里面的StandardContextif (context instanceof TomcatEmbeddedContext) {TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;//把TomcatStarter添加到StandardContext的一个成员变量starter中embeddedContext.setStarter(starter);embeddedContext.setFailCtxIfServletStartFails(true);}//把TomcatStarter添加到StandardContext的initializers中,//initializers是一个Mapcontext.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) {….//这里会去启动tomcatinitialize();}
TomcatWebServer#initialize():
private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));。。。// 启动tomcatthis.tomcat.start();//启动一个阻塞线程,防止tomcat退出startDaemonAwaitThread();。。。}
Tomcat启动以后,会执行到tomcat里面的StandardContext#startInternal():
protected synchronized void startInternal() throws LifecycleException {...// initializers是前面存进去的,里面只有一个TomcatStarterfor (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():
@Overridepublic void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {try {// initializers也是前面存进去的,里面一共3个,其中一个是ServletWebServerApplicationContext里面的selfInitializefor (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);//这里面就拿到了DispatcherServletRegistrationBeanfor (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);//这里去加载DispatcherServletRegistrationBeanaddServletContextInitializerBeans(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)) {//这里就有DispatcherServletRegistrationBeanaddServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);}}}
拿到了DispatcherServletRegistrationBean,上面继续执行它的onStartup(),RegistrationBean#onStartup():
@Overridepublic final void onStartup(ServletContext servletContext) throws ServletException {//注册DispatcherServletregister(description, servletContext);}
DynamicRegistrationBean# register():
@Overrideprotected final void register(String description, ServletContext servletContext) {//注册DispatcherServletD registration = addRegistration(description, servletContext);if (registration == null) {logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");return;}//设置映射路径和启动级别configure(registration);}
ServletRegistrationBean#configure():
@Overrideprotected 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。




