赞
踩
本文通过对spring-boot-starter-web自动配置类的梳理,结合Spring Boot Application的启动过程,明确了Tomcat Web Server的创建和启动过程。
AutoConfiguration类主要有WebMvcAutoConfiguration,DispatcherServletAutoConfiguration, ServletWebServerFactoryAutoConfiguration。
注册WebApplicationContextServletContextAwareProcessor的Bean到Bean Factory,该PostProcessorBean为后续创建的实现ServletContextAware接口的bean设置ServletContext对象,为实现ServletConfigAware接口的bean设置ServletConfig对象。
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#postProcessBeanFactory
/**
* Register ServletContextAwareProcessor.
* @see ServletContextAwareProcessor
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(
// org.springframework.boot.web.servlet.context.WebApplicationContextServletContextAwareProcessor
new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
registerWebApplicationScopes();
}
org.springframework.web.context.support.ServletContextAwareProcessor#postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (getServletContext() != null && bean instanceof ServletContextAware) {
((ServletContextAware) bean).setServletContext(getServletContext());
}
if (getServletConfig() != null && bean instanceof ServletConfigAware) {
((ServletConfigAware) bean).setServletConfig(getServletConfig());
}
return bean;
}
通过自动配置类ServletWebServerFactoryAutoConfiguration创建TomcatServletWebServerFactory。
通过调用TomcatServletWebServerFactory的getWebServer方法获取TomcatWebServer。
如果使用可执行jar启动,servletContext为空,创建ServletWebServerFactory,生成WebServer对象。
如果使用war部署,servletContext不为空,只进行servlet context初始化
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); // 如果使用可执行jar启动,servletContext为空,创建ServletWebServerFactory,生成WebServer对象。 if (webServer == null && servletContext == null) { // 获取ServletWebServerFactory,如果底层容器是tomcat,返回TomcatServletWebServerFactory。 ServletWebServerFactory factory = getWebServerFactory(); // 通过ServletWebServerFactory创建WebServer this.webServer = factory.getWebServer(getSelfInitializer()); } // 如果使用war部署,servletContext不为空,只进行servlet context初始化 else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
以下代码只有在使用可执行jar(嵌入式tomcat)时才会执行
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }
org.springframework.boot.web.embedded.tomcat.TomcatWebServer#TomcatWebServer(org.apache.catalina.startup.Tomcat, boolean)
/** * Create a new {@link TomcatWebServer} instance. * @param tomcat the underlying Tomcat server * @param autoStart if the server should be started */ 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(); } }); // 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(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
org.springframework.boot.web.embedded.tomcat.TomcatWebServer#start
@Override public void start() throws WebServerException { synchronized (this.monitor) { if (this.started) { return; } try { addPreviouslyRemovedConnectors(); Connector connector = this.tomcat.getConnector(); if (connector != null && this.autoStart) { performDeferredLoadOnStartup(); } checkThatConnectorsHaveStarted(); this.started = true; logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '" + getContextPath() + "'"); } catch (ConnectorStartFailedException ex) { stopSilently(); throw ex; } catch (Exception ex) { throw new WebServerException("Unable to start embedded Tomcat server", ex); } finally { Context context = findContext(); ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } } }
通过下面的类图可以发现可以通过DispatcherServletRegistrationBean注册DispatcherServlet,通过FilterRegistrationBean注册Filter。
org.springframework.boot.web.servlet.ServletRegistrationBean
A ServletContextInitializer to register Servlets in a Servlet 3.0+ container. Similar to the registration features provided by ServletContext but with a Spring Bean friendly design.
The servlet must be specified before calling onStartup. URL mapping can be configured used setUrlMappings or omitted when mapping to ‘/*’ (unless alwaysMapUrl is set to false). The servlet name will be deduced if not specified.
org.springframework.boot.web.servlet.ServletContextInitializer
Interface used to configure a Servlet 3.0+ context programmatically. Unlike WebApplicationInitializer, classes that implement this interface (and do not implement WebApplicationInitializer) will not be detected by SpringServletContainerInitializer and hence will not be automatically bootstrapped by the Servlet container.
This interface is primarily designed to allow ServletContextInitializers to be managed by Spring and not the Servlet container.
org.apache.catalina.core.StandardContext#getServletContext
/**
* @return the servlet context for which this Context is a facade.
*/
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return context.getFacade();
}
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
private void selfInitialize(ServletContext servletContext) throws ServletException { // 将当前ApplicationContext设置到ServletContext // servletContext.setAttribute( // WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this); prepareWebApplicationContext(servletContext); // ServletContextScope: Scope wrapper for a ServletContext, i.e. for global web application attributes. // This scope is registered as default scope with key "application". registerApplicationScope(servletContext); // Register web-specific environment beans ("contextParameters", "contextAttributes") with the given BeanFactory, as used by the WebApplicationContext. WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { // 调用onStartUp beans.onStartup(servletContext); } }
org.springframework.boot.web.servlet.ServletContextInitializerBeans#ServletContextInitializerBeans
@SafeVarargs public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) { this.initializers = new LinkedMultiValueMap<>(); this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class); // 添加Servlet addServletContextInitializerBeans(beanFactory); // 添加Filter等 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); }
执行完毕后initializers字段的值。
可参考 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.
There is one context per “web application” per Java Virtual Machine. (A “web application” is a collection of servlets and content installed under a specific subset of the server’s URL namespace such as /catalog and possibly installed via a .war file.)
SpringBoot深入(一)–SpringBoot内置web容器及配置
spring-boot定制和优化内嵌的Tomcat
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。