Spring Boot版本: 2.0.0.RELEASE
这里需要引入依赖 spring-boot-starter-web
这里有可能有个人的误解,请抱着怀疑态度看。
建议: 感觉自己也会被绕晕,所以有兴趣的使用IDE工具看下源码
在 ServletWebServerApplicationContext
中有段代码,如下:
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext @Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } /** * 创建Web服务容器,如:Tomcat,Jetty等;具体创建的容器根据#getWebServerFactory得到 * 而WebServerFactory在BeanFactory获取,也就是在加载Bean时确定的, * 这里通过Spring Boot自动配置了Tomcat,如果想要深入可以追着#getWebServerFactory看 * 下面有对应的不太详细的解释 */ private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); } // ..... }
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration.EmbeddedTomcat#tomcatServletWebServerFactory /** * 这里就通过自动加载Bean时加载了关于Tomcat的WebServerFactory * 至于为什么加载Tomcat而不是Jetty,就要多谢Bean加载时@ConditionalOnClass注解 * 因为我们引入依赖spring-boot-starter-web 其次,它又引入了 spring-boot-starter-tomcat依赖 * 因为存在{ Servlet.class, Tomcat.class, UpgradeProtocol.class }这些class,所以加载Tomcat */ @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); } } // .......
上述中说明了 Tomcat
的创建,而什么时候调用 onRefresh
并创建 Tomcat
呢?
其实 onRefresh
是 org.springframework.context.support.AbstractApplicationContext
一个未具体实现方法,交给子类实现,它在调用 refresh()
方法中调用。
而 org.springframework.boot.SpringApplication#run(java.lang.String...)
调用了 refreshContext
,又间接调用上述 refresh()
方法
在 onRefresh
调用后在 refresh()
中还调用了 finishRefresh()
,而重写了其方法 finishRefresh
的 ServletWebServerApplicationContext
就启动了Web服务,代码如下:
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#finishRefresh @Override protected void finishRefresh() { super.finishRefresh(); WebServer webServer = startWebServer(); if (webServer != null) { publishEvent(new ServletWebServerInitializedEvent(webServer, this)); } } private WebServer startWebServer() { WebServer webServer = this.webServer; if (webServer != null) { // 这里如何启动具体可看org.springframework.boot.web.embedded.tomcat.TomcatWebServer // 这里之所以我们启动了Spring Boot后程序还在运行是因为Tomcat启动线程在后台运行 webServer.start(); } return webServer; }
org.springframework.boot.web.embedded.tomcat.TomcatWebServer public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); } private void initialize() throws WebServerException { //..... // Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown // 大概意思是创建一个非守护线程来运行吧 startDaemonAwaitThread(); //.... } private void startDaemonAwaitThread() { Thread awaitThread = new Thread("container-" + (containerCounter.get())) { @Override public void run() { TomcatWebServer.this.tomcat.getServer().await(); } }; awaitThread.setContextClassLoader(getClass().getClassLoader()); awaitThread.setDaemon(false); awaitThread.start(); }
上面就大概说了Tomcat怎么在Spring Boot启动后加载创建的
平时使用SSM开发时,使用Spring MVC 我们知道需要配置 DispatcherServlet
,而这里的是通过自动配置的,还对 DispatcherServlet
通过 ServletRegistrationBean
类进行封装,这里自动装配的代码在 org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
中可见,代码如下:
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration @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; } org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration /** * 这里通过构造器获取已经注册的 DispatcherServlet Bean * 然后封装在ServletRegistrationBean */ @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; }
封装了Servlet的 ServletRegistrationBean
各个Bean又会被 ServletContextInitializerBeans
进行管理。
在上述 第一点中Tomcat创建加载
中有个方法没有详述,就是 ServletWebServerApplicationContext
中的 createWebServer
方法,里面调用了一个 getSelfInitializer
方法,使用返回值作为参数,代码如下:
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext private void createWebServer() { //... this.webServer = factory.getWebServer(getSelfInitializer()); //... } /** * Returns the {@link ServletContextInitializer} that will be used to complete the * setup of this {@link WebApplicationContext}. * @return the self initializer * @see #prepareWebApplicationContext(ServletContext) */ private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { // 又封装成了ServletContextInitializer return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { //.... //getServletContextInitializerBeans这里就能获取到封装了Servlet或Filter等的ServletContextInitializer for (ServletContextInitializer beans : getServletContextInitializerBeans()) { // 这里通过回调Servlet或Filter能够获取到servletContext,并且将自己(Servlet或Filter)注册到servletContext,这里可能要去了解下Tomcat了 beans.onStartup(servletContext); } } /** * Returns {@link ServletContextInitializer}s that should be used with the embedded * web server. By default this method will first attempt to find * {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain * {@link EventListener} beans. * @return the servlet initializer beans */ protected Collection<ServletContextInitializer> getServletContextInitializerBeans() { // 这里在new ServletContextInitializerBeans 时会将BeanFactory给它 // 它在构造器中将封装了Servlet或Filter等的ServletContextInitializer子类获取 // 并放入一个成员变量sortedList中 return new ServletContextInitializerBeans(getBeanFactory()); }
到此,大概就知道了Spring MVC中 DispatcherServlet
是怎么进入Tomcat的了,如果还想细究 DispatcherServlet
是怎么被一步步置入Tomcat容器中的,可以看下 org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
中的代码,这里不展开了。
入职后好久没运动,被同事拉去打羽毛球,结果,气喘不上来,脑晕,感觉不好,第一天全身酸痛,第二天更痛。我认为,入职后第一条建议就是找时间运动。
这篇其实接着第一篇文章 我的Spring Boot学习记录(一):自动配置的大致调用过程 就想要写的了,拖了好久。期间问自己,为什么干这种无趣的东西。不论如何,还是抽了时间写了,就这样吧。
这里有可能有个人的误解,请抱着怀疑态度看。
建议: 感觉自己也会被绕晕,所以有兴趣的使用IDE工具看下源码