SpringBoot-嵌入式Servlet容器原理
WebServerFactory自动配置
@Configuration @AutoConfigureOrder(-2147483648) @ConditionalOnClass({ServletRequest.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) @EnableConfigurationProperties({ServerProperties.class}) @Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class}) public class ServletWebServerFactoryAutoConfiguration { // Servlet Web Server 通用配置,将ServerProperties注入到定制器中,实现application.properties中的配置信息配置 Servlet Web Server @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); } // Tomcat Servlet Web Server 配置信息 @Bean @ConditionalOnClass( name = {"org.apache.catalina.startup.Tomcat"} ) public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } }
EmbeddedTomcat 嵌入式Tomcat
@Configuration // 判断当前是否引入了 org.apache.catalina.startup.Tomcat 依赖 @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class}) // 当没有 ServletWebServerFactory 类型的Bean时才注入该组件 @ConditionalOnMissingBean( value = {ServletWebServerFactory.class}, search = SearchStrategy.CURRENT ) public static class EmbeddedTomcat { public EmbeddedTomcat() { } // 创建Tomcat容器工厂 @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); } }
ServletWebServerFactory(Servlet容器工厂) 继承关系
@FunctionalInterface public interface ServletWebServerFactory { // 创建WebServer即Servlet容器 WebServer getWebServer(ServletContextInitializer... initializers); }
WebServer (Servlet容器) 继承关系
public interface WebServer { void start() throws WebServerException; void stop() throws WebServerException; int getPort(); }
TomcatServletWebServerFactory 获得Tomcat Servlet 容器(即org.apache.catalina.startup.Tomcat)
public WebServer getWebServer(ServletContextInitializer... initializers) { // 创建Tomcat容器 Tomcat tomcat = new Tomcat(); // 配置Tomcat运行环境 File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(), initializers); // 调用 tomcat.start(); 启动容器 return this.getTomcatWebServer(tomcat); }
修改Servlet容器配置信息的方式
1)修改application.properties
server.port=8888
2) 添加定制器
package com.wjz.config; import org.springframework.boot.web.server.ConfigurableWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.stereotype.Component; @Component public class WebServerConfig implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> { @Override public void customize(ConfigurableWebServerFactory factory) { factory.setPort(8888); } }
定制器后置处理器
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebServerFactory) { this.postProcessBeforeInitialization((WebServerFactory)bean); } return bean; } private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> { customizer.customize(webServerFactory); }); } private Collection<WebServerFactoryCustomizer<?>> getCustomizers() { if (this.customizers == null) { this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans()); this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE); this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers; } private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() { return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values(); } }
Servlet容器启动流程
什么时候创建ServletWebServerFactory(Servlet容器工厂),什么时候创建 WebServer (Servlet容器)并启动?
1)EmbeddedTomcat.TomcatServletWebServerFactory处和TomcatServletWebServerFactory#getWebServer()处打断点,debug方式启动系统
2)创建SpringApplication对象并执行run()方法
3)context = this.createApplicationContext(); this.refreshContext(context); SpringBoot刷新IOC容器即创建IOC容器对象并初始化容器,创建容器中的每一个组件
如果是Web环境创建AnnotationConfigServletWebServerApplicationContext,否则创建默认的IOC容器AnnotationConfigApplicationContext
4)this.refresh(context); 刷新刚创建好的IOC容器即执行IOC容器的refresh()方法
5)this.onRefresh(); Web容器会创建嵌入式Servlet Web Server容器(参见ServletWebServerApplicationContext)
6)ServletWebServerFactory factory = this.getWebServerFactory(); 创建ServletWebServerFactory(Servlet容器工厂),后置处理器工作获取所有的定制器定制Servlet容器的相关配置
7)this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); 创建 WebServer (Servlet容器)并启动
外部Servlet容器&支持JSP
1)创建一个war项目
2)嵌入式Tomcat依赖指定为provided
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
3)编写一个SpringBootServletInitializer的子类并复写configure方法
package com.wjz.springboot; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBootJspApplication.class); } }
4)启动外部Tomcat
外部Servlet容器启动流程
1)Servlet3.0的一个规范,容器启动时会创建每一个应用实现了javax.servlet.ServletContainerInitializer的实例
2)寻找各个应用每个jar包路径 META-INF/services/ 路径下的文件,文件名为 javax.servlet.ServletContainerInitializer,内容是实现类的全类名,参考 spring-web jar包下对应的路径文件
org.springframework.web.SpringServletContainerInitializer
3)还可以使用@HandlesTypes注解,在容器启动时传入类
4)SpringServletContainerInitializer将@HandlesTypes({WebApplicationInitializer.class})标注的这个类型的所有类传入到onStartup方法的Set<Class<?>>中,
将这些类型创建实例且调用各自的onStartup方法
相当于SpringBootServletInitializer类会创建对象并调用onStartup方法,该方法中会创建IOC容器
public void onStartup(ServletContext servletContext) throws ServletException { this.logger = LogFactory.getLog(this.getClass()); // 创建IOC容器 WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext); if (rootAppContext != null) { servletContext.addListener(new ContextLoaderListener(rootAppContext) { public void contextInitialized(ServletContextEvent event) { } }); } else { this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context"); } }