【springboot源码】嵌入式Servlet容器底层原理分析
注:本文阅读前提,对springboot基本使用有所了解,此外,其他一些源码解读,如果有需要,可以参考:
- 【springboot源码】springboot自动配置原理分析
- 【Spring源码】 后置处理器BeanPostProcessor底层原理分析
- 【spring源码】spring声明式事务底层源码分析
- 【spring源码】ApplicationListener事件监听底层原理
- 【spring源码】AOP底层源码分析
- 【spring源码】spring IOC容器底层源码分析
- 【SpringMVC源码】SpringMVC核心DispatcherServlet底层源码分析
- 【mybatis源码】 mybatis底层源码分析
1.springboot嵌入式Servlet容器基础使用
- SpringBoot默认使用Tomcat作为嵌入式的Servlet容器;
1.1 SpringBoot定制和修改Servlet容器的相关配置
- 第一种方法,是在配置文件application.Properties中修改和server有关的配置(源码底层:ServerProperties类)
#1.tomcat端口
server.port=8009
#2.项目访问路径
server.servlet.context-path=/demo
#指定错误页面
server.error.path=/error
#设置session最大超时时间,默认为30分钟
#server.servlet.session.timeout=
#该服务绑定的IP地址
#server.address=Ip
#tomcat的编码
server.tomcat.uri-encoding=UTF-8
#tomcat最大连接数,默认200
server.tomcat.max-connections=250
#存放临时文件
#server.tomcat.basedir=路径
#打开tomcat 的access日志
#server.tomcat.accesslog.enabled=true
#设置access格式
#server.tomcat.accesslog.pattern=commen
#设置access路径
#server.tomcat.accesslog.directory=路径
#日志存放位置
#logging.path=路径
#日志名称
#logging.file=my.log
- 第二种方法,是在配置类中编写一个:WebServerFactoryCustomizer嵌入式的Servlet容器的定制器bean;来修改Servlet容器的配置
//配置嵌入式的Servlet容器
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8081);
}
};
}
- 其实第一种配置方式底层原理跟第二种一样,具体会在下面聊底层原理时讲。
1.2 springboot注册Servlet三大组件【Servlet、Filter、Listener】方法
- 由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件,所以无法以传统方式注入。
- springboot中注册三大组件用以下方式:
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
return registrationBean;
}
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return registrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return registrationBean;
}
2.springboot嵌入式Servlet容器自动配置原理
- springboot的自动装配功能会在项目启动的时候进行自动配置,具体原理分析可见:
- 【springboot源码】springboot自动配置原理分析
- 在整个自动装配过程中,嵌入式Servlet容器自动配置也包含在其中。
- 而关于Servlet容器自动配置类就是:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.
*/
public static class BeanPostProcessorsRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry,
"webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry,
"errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(
this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
}
- @Configuration,标注在哪个类上就代表它是个配置类,会在包扫描时被识别。
- @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)装配先后排序
- @ConditionalOnClass(ServletRequest.class)表示这个配置类只有在项目中存在ServletRequest.class这个类时才会生效
- @ConditionalOnWebApplication(type = Type.SERVLET)代表这个配置类只有本项目是web项目时才生效
- @EnableConfigurationProperties(ServerProperties.class)在本配置类装配前,先装配ServerProperties这个配置类,那我们来打开这个ServerProperties看看:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
private Boolean useForwardHeaders;
private String serverHeader;
...}
-
这个配置类就是读取我们配置文件中像server.port=8009这样的server配置的。
-
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })这个注解是在向容器中添加组件: -
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class注册一个后置处理器,这里先打个记号,我们稍后再说它的作用,
-
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,配置Tomcat的
-
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,配置Jetty的
-
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class,配置Undertow的
-
我们来打开ServletWebServerFactoryConfiguration这个类看看:
@Configuration
class ServletWebServerFactoryConfiguration {
@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();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {
@Bean
public JettyServletWebServerFactory JettyServletWebServerFactory() {
return new JettyServletWebServerFactory();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
return new UndertowServletWebServerFactory();
}
}
}
- 上面我们讲过@ConditionalOnClass这个注解标注在配置类上,那被标注的配置类生效的前提就是项目中必须有@ConditionalOnClass括号中标记的类。
- 我来截一张我看源码时的图片,大家可以一起看看:
- 看到我们标注的@ConditionalOnClass注解了吗?里面的类在我的IDE里是红色的,代表我的IDE没有在项目里找个相应的jar包,相应的类,所以默认情况下EmbeddedJetty ,EmbeddedUndertow 这两个组件注入都不会成功。
- 成功的只有一个,就是:
- EmbeddedTomcat :Tomcat的配置,它向容器中注入了一个TomcatServletWebServerFactory,Tomcat工厂,它里面有段代码,我们可以看看:
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
//创建一个Tomcat
Tomcat tomcat = new Tomcat();
//配置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);
//将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器
return getTomcatWebServer(tomcat);
}
- 创建一个Tomcat,配置并返回,这个方法的调用,我们会在下个章节,聊嵌入式Servlet容器启动原理的时候讲
- 现在我们回过头,再聊一下我们之前的那个标记:ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
- 我们打开它看看它都做了什么:
public static class BeanPostProcessorsRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
//重点
registerSyntheticBeanIfMissing(registry,
"webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry,
"errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(
this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
- 重点在registerSyntheticBeanIfMissing(registry,“webServerFactoryCustomizerBeanPostProcessor”,WebServerFactoryCustomizerBeanPostProcessor.class);它往容器注入了一个WebServerFactoryCustomizerBeanPostProcessor
- WebServerFactoryCustomizerBeanPostProcessor从名字看就知道这是个后置处理器,至于后置处理器的作用,它可以在所有bean初始化之前做一些操作,详细可见:
- 【Spring源码】 后置处理器BeanPostProcessor底层原理分析
- 这里我们打开它:
public class WebServerFactoryCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
//给WebServerFactoryCustomizerBeanPostProcessor设置BeanFactory 的,这里我们不看
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"WebServerCustomizerBeanPostProcessor can only be used "
+ "with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
//初始化之前,重点
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//如果当前初始化的是一个WebServerFactory类型的组件
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值;
LambdaSafe
.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
//从容器中获取所有这葛类型的组件:WebServerFactoryCustomizer
//定制Servlet容器,给容器中可以添加一个WebServerFactoryCustomizer类型的组件
return (Collection) this.beanFactory
.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
}
- 在容器初始化所有bean之前先判断是不是WebServerFactory类型的,如果是的话就找到容器中所有的WebServerFactoryCustomizer,依次调用customize()方法把定制配置加载到这个WebServerFactory类型的Servlet容器中。
- 至于都那些bean是WebServerFactory类型的,像上面的TomcatServletWebServerFactory就是,跟踪到底层就会发现它是继承于WebServerFactory的。
- 对后置处理器熟悉的,看我代码中的注释应该就可以明白了,还记得上面第一个章节中讲的两种可以修改配置的方法吗?
- 第二种,是在配置类中编写一个:WebServerFactoryCustomizer嵌入式的Servlet容器的定制器bean;来修改Servlet容器的配置,到这里应该明白了吧
- 然后,第一种修改配置的方法的底层原理,让我们回到最开始的那个入口配置类ServletWebServerFactoryAutoConfiguration,里面有注入这么个bean :
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
- 我们打开ServletWebServerFactoryCustomizer:
public class ServletWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final ServerProperties serverProperties;
public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
@Override
public int getOrder() {
return 0;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.serverProperties::getPort).to(factory::setPort);
map.from(this.serverProperties::getAddress).to(factory::setAddress);
map.from(this.serverProperties.getServlet()::getContextPath)
.to(factory::setContextPath);
map.from(this.serverProperties.getServlet()::getApplicationDisplayName)
.to(factory::setDisplayName);
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters)
.to(factory::setInitParameters);
}
}
- 同样的实现了WebServerFactoryCustomizer
- 总结一下步骤:
1,SpringBoot根据导入的jar依赖情况,给容器中添加相应的Servlet容器工厂【TomcatServletWebServerFactory】
2,容器中某个组件要创建对象就会惊动后置处理器WebServerFactoryCustomizerBeanPostProcessor,如果这个组件是Servlet容器工厂,比如TomcatServletWebServerFactory,那这个后置处理器就会开始工作。
3,这个后置处理器会把容器里的所有WebServerFactoryCustomizer找出来,调用定制器的定制方法,把定制配置加载到Servlet容器工厂中。
4,最后在Servlet容器工厂初始化后创建Servlet容器(tomcat)时使用那些配置。
3.springboot嵌入式Servlet容器启动原理
- 上面聊了嵌入式Servlet容器是怎样自动配置的,现在我们接着聊一下它的启动原理:
@SpringBootApplication
public class ApplicationStart {
public static void main(String[] args) {
SpringApplication.run(ApplicationStart.class, args);
}
}
- 从springboot启动类入手,进入SpringApplication.run(ApplicationStart.class, args);方法一直跟进去:
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主配置类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前是否一个web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起 来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
- 这段会加载一些配置,创建SpringApplication对象
- 创建SpringApplication对象后,会进入一个run方法:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//获取SpringApplicationRunListeners;从类路径下META‐INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准 备完成
configureIgnoreBeanInfo(environment);
//打印Banner 图就是springboot项目启动时那个springboot标志
Banner printedBanner = printBanner(environment);
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//所有的SpringApplicationRunListener回调started方法
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
- 代码中我写的注释已经很详细了,其中涉及到嵌入式Servlet容器启动的是refreshContext(context);这行代码,刷新IOC容器,也就是IOC加载所有bean的过程,具体spring IOC底层可参考:
- 【spring源码】spring IOC容器底层源码分析
下面整个底层原理的剖析建立在对spring IOC容器熟悉的情况下
- 现在我们回过头,首先,这是个web项目所以在context = createApplicationContext();这行代码的时候创建的IOC容器是web容器,就是这个:AnnotationConfigServletWebServerApplicationContext
- AnnotationConfigServletWebServerApplicationContext比之正常IOC容器重写了onRefresh方法:
@Override
protected void onRefresh() {
super.onRefresh();
try {
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) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
- 上面代码内容很简单,就是找到容器内的ServletWebServerFactory (比如我们上面聊的TomcatServletWebServerFactory),然后创建它(初始化它之前会调用相关加载定制配置的后置处理器),最后调用它的getWebServer()方法启动Servlet容器。
- 我们可以再回顾一下TomcatServletWebServerFactory的getWebServer方法
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
//创建一个Tomcat
Tomcat tomcat = new Tomcat();
//配置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);
//将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器
return getTomcatWebServer(tomcat);
}
4.springboot使用外置的Servlet容器
- 嵌入式Servlet容器:应用打成可执行的jar
优点:简单、便携
缺点:默认不支持JSP、优化定制比较复杂
- 外置的Servlet容器:外面安装Tomcat—应用要打成war包;
4.1 步骤
-
1,必须创建一个springboot war项目;
-
2, 将嵌入式的Tomcat指定为provided;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
- 3,必须编写一个SpringBootServletInitializer的子类,并调用configure方法
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
//传入SpringBoot应用的主程序
return application.sources(SpringBoot04WebJspApplication.class);
}
}
- 4, 启动服务器就可以使用;
4.2 原理
- servlet3.0 规则:
1、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:
2、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
3、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;
- 流程:
1、启动Tomcat
2、org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer:
Spring的web模块里面有这个文件:org.springframework.web.SpringServletContainerInitializer
3、SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set<Class<?>>;为这些WebApplicationInitializer类型的类创建实例;
4、每一个WebApplicationInitializer都调用自己的onStartup;
5、相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法
6、SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建SpringApplication
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
//1、创建SpringApplicationBuilder
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
//调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
builder = configure(builder);
//使用builder创建一个Spring应用
SpringApplication application = builder.build();
if (application.getSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.getSources().add(getClass());
}
Assert.state(!application.getSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.getSources().add(ErrorPageFilterConfiguration.class);
}
//启动Spring应用
return run(application);
}
7、Spring的应用就启动并且创建IOC容器
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新IOC容器
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
【完】