SpringBoot——启动与自动配置类查找中启动过程,已经找到了tomcat启动在于AbstractApplicationContext.refresh()的onRefresh()方法中,即ServletWebServerApplicationContext.onRefresh()方法中。
下面先回顾下tomcat+springMVC启动web服务的流程实现,然后在学习springboot中的tomcat嵌入。
一、tomcat+SpringMVC启动web服务
Web服务的核心流程其实就是servlet的生命周期:
① tomcat启动,解析web.xml文件中DispatcherServlet生成Servlet对象,放入到tomcat容器中
② 调用DispatcherServlet.init():初始化SpringIOC容器;初始化SpringMVC的9个组件并放入到SpringIOC容器中。
③ 响应服务DispatcherServlet.service()
④ 容器关闭DispatcherServlet.destory()
其中②③是需要具体了解的,前面SpringMVC——DispatcherServlet中已经学习过②中9个组件初始化+③servlet.service()实现,但是还没有明确②中springIOC容器与Servlet的关系
下面借用官网的一个例子说明:
a、创建一个SpringMVC的IOC容器WebApplicationContext,并初始化WebApplicationContext.refresh()(这里有个WebApplicationContext.setparent(rootApplicationContext))
b、将WebApplicationContext绑定到DispatcherServlet中
c、初始化SpringMVC的9个组件(包括HandlerMapping,HandlerAdapters等)并放入到WebApplicationContext容器中。
public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletCxt) { //创建一个SpringMVC的容器WebApplicationContext,并初始化 AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(AppConfig.class); ac.refresh(); // 将springMVC的容器WebApplicationContext绑定到DispatcherServlet中 DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
综上:如果我们要实现tomcat的嵌入,可能需要实现:
① 需要new Tomcat、需要new DispatcherServlet、需要new WebApplicaitonContext
② 然后将webApplicationContext绑定到DispatcherServlet中,将DispatcherServlet放入到Tomcat中。
伪代码实现流程:
ApplicationContext context = new ApplicationContext(); context.refresh(); DispatcherServlet servlet = new DispathcherServlet(context); Tomcat tomcat = new Tomcat(); tomcat.addServlet(servlet);//主要是这一步 tomcat.start();
二、tomcat嵌入
ServletWebServerApplicationContext.onRefresh():tomcat启动入口
/*org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh */ protected void onRefresh() { //父类方法,设置主题资源 super.onRefresh(); try { //tomcat启动 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //自动配置类ServletWebServerFactoryAutoConfiguration //@Import ServletWebServerFactoryConfiguration.EmbeddedTomcat.class //初始化TomcatServletWebServerFactory到SpringIOC容器中 //注意初始化多个ServletWebServerFactory会报错 ServletWebServerFactory factory = getWebServerFactory(); //工厂方法创建webserver //Tomcat tomcat = new tomcat(); //tomcat的属性修改 //tomcat.start() //另启动一个端口接受tomcat停止命令 this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { // getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } //根据environment初始化servletContext的属性 initPropertySources(); }
TomcatServletWebServerFactory.getWebServer(getSelfInitializer()):
/* org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer */ public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } //创建tomcat,默认端口8080,new出来 Tomcat tomcat = new Tomcat(); //创建临时路径,存放tomcat的log日志和dump文件,可用server.tomcat.basedir配置路径 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); //创建Connector,协议http1.1,
//Connector.setPort("server.port"),server.port生效的地方 //connector编码encoding、SSL(https)支持
//server.xml 中<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector);
//server.xml 中<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" /> tomcat.getHost().setAutoDeploy(false);
//server.xml 中<Engine name="Catalina" defaultHost="localhost"></Engine> configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { //添加自定义的Connector到tomcat中,不会有
//server.xml <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }
getTomcatWebServer():
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { //创建一个tomcat的web服务, //tomcat服务启动tomcat.start() //创建一个非阻塞守护线程,BIO监听端口8005,接受字节"SHUTDOWN",停止当前tomcat return new TomcatWebServer(tomcat, getPort() >= 0); } 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 { 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(); } }); // tomcat启动 this.tomcat.start(); rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } catch (NamingException ex) { } // 与Jetty不同,tomcat线程都是守护线程,我们创建一个阻塞的非守护线程来关闭它。 // (守护线程在所有非守护线程关闭后,会自动关闭,所以这里非守护线程最重要的作用是维持tomcat非守护线程的运行)。 startDaemonAwaitThread(); } catch (Exception ex) { stopSilently(); destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
以上就是tomcat启动流程。tomcat.start()会执行this.webServer = factory.getWebServer(getSelfInitializer());中的getSelfInitializer()
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { //rootApplicationContext.setServletContext(servletContext) //servletContext.setAttribute(rootApplicationContext) //这里将webApplicationContext作为rootApplicationContext放入到了ServletContext中 //后面DispatcherServlet.init()时,会初始化一个applicationContext //然后applicationContext.setParent(ServletContext.getAttribute(rootApplicationContext)) prepareWebApplicationContext(servletContext); //注册Servlet作用域application到BeanFactory中,前面研究springboot启动时注入过session+request registerApplicationScope(servletContext); //注册servlet环境的bean到BeanFactory中 //servletContext,servletConfig,contextAttributes WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
getServletContextInitializerBeans():获取BeanFactory中的DispatcherServlet
/* org.springframework.boot.web.servlet.context.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); //initializerTypes = [ServletContextInitializer.class] //扫描BeanFactory中的ServletContextInitializer.class类型的Bean放入到initializers中 //DispatcherServletRegistrationBean //FilterRegistrationBean 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); }
addServletContextInitializerBeans(beanFactory):
/* org.springframework.boot.web.servlet.ServletContextInitializerBeans#addServletContextInitializerBeans */ private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) { for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) { for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, initializerType)) { addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory); } } } private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) { if (initializer instanceof ServletRegistrationBean) { //扫描Servlet到initializers中,"dispatcherServlet" Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet(); addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof FilterRegistrationBean) { //扫描filter到initializers中, Filter source = ((FilterRegistrationBean<?>) initializer).getFilter(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof DelegatingFilterProxyRegistrationBean) { String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof ServletListenerRegistrationBean) { EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener(); addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source); } else { addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer); } }
DispatcherServletRegistrationBean初始化:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) public class DispatcherServletAutoConfiguration { /* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; /* * The bean name for a ServletRegistrationBean for the 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 { @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) { // 创建dispatcherServlet实例,设置dispatcherServlet的属性, // WebMvcProperties对应application.properties中的"spring.mvc.dispatchOptionsRequest" // spring.mvc.dispatch_options_request = true // spring.mvc.dispatch_trace_request = false // spring.mvc.throw_exception_if_no_handler_found = false // spring.mvc.publish_request_handled_events = true // spring.http.log_request_details = false DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails()); return dispatcherServlet; } @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver) { // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } } @Configuration(proxyBeanMethods = false) @Conditional(DispatcherServletRegistrationCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) @Import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) { //封装dispatcherServlet实例, //spring.mvc.servlet.path = / //spring.mvc.servlet.load-on-startup = -1 //multipartConfg DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); multipartConfig.ifAvailable(registration::setMultipartConfig); return registration; } } //....... }
总结:
① tomcat嵌入:是AbstactApplicationContext.onRefresh()中启动的
Tomcat tomcat = new Tomcat(); //server.xml的属性设置到tomcat //包括server.port设置到Connector中 tomcat.start(); //start启动会执行selfInitialize(),tomcat--dispatcherServlet--springbootIOC容器,的绑定都是在selfInitalize()里面实现的,
// ① ServletContext.setAttribute("rootApplicationContext",springbootApplicationContext) // ② 扫描beanfactory中的ServletContextInitializer.class类型Servlet/Filter // ③ DispatcherServletRegistrationBean中dispatcherServlet放入了tomcat中
// ④ dispatcherServlet.init()时new WebApplicationContext().setParent(servletContext.getAttribute("rootApplicationContext"))
②tomcat.start()启动是一个监听8080端口的守护线程(非阻塞NIO),后面启动了一个非守护线程监听8005端口(阻塞BIO),作用:
- 当守护线程关闭后,非守护线程会全部关闭。开一个非守护线程(8005),可维持守护线程(8080)一直运行。
- 非守护线程(8005)还会接受"SHUTDOWN”字节指令,关闭非守护线程,从而关闭守护线程