1. SpringBoot中怎么启动Tomcat?
1.1 ServletWebServerFactoryAutoConfiguration
配置Servlet web容器。
@Configuration(proxyBeanMethods = false)
@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 {
}
-
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class 导入
WebServerFactoryCustomizerBeanPostProcessor
组件,web服务工厂定制器的后置增强 -
导入了三种不同的web服务器(Tomcat、Jetty、Undertow),默认是Tomcat
我们来看一下 EmbeddedTomcat
:
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
}
tomcatServletWebServerFactory()
方法中的参数都是从容器中获取,我们可以自定义直接放到容器中。实现ServletWebServerFactory
接口,该接口的getWebServlet()方法就是创建 Tomcat容器。
1.2 DispatcherServletAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.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) {
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;
}
}
}
dispatcherServlet
方法就是往容器中注入DispatcherServlet
组件。- dispatcherServletRegistration() 方法就是将将
DispatcherServlet
组件添加到Tomcat中(tomcat.addServlet()
)
我们来看一下DispatcherServletRegistrationBean
继承图:
在Tomcat启动的时候 会调用 ServletContextInitializer.onStartup() 方法回调,将DispatcherServlet 添加到 Tomcat中,后面的启动流程就跟SpringMVC的启动流程是一样的
2. 核心方法run()
下面是一个简单的项目,我们直接开始看run方法
@SpringBootApplication
public class SpringbootDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemoApplication.class, args);
Object demoService = run.getBean("demoService");
System.out.println("demoService = " + demoService);
}
}
我们这里直接看 SpringApplication#run(java.lang.Class<?>[], java.lang.String[])
方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
我们这里先看一下 SpringApplication
的构造函数流程:
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环境、web环境、reactive环境三种。其判断逻辑就是判断是否存在指定的类,默认是Servlet 环境,我们这也是Servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// getSpringFactoriesInstances 方法加载了 spring.factories文件。在这里进行了首次加载spring.factoies文件。设置 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 获取监听器,也加载了spring.factories文件
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 设置启动类信息
this.mainApplicationClass = deduceMainApplicationClass();
}
我们下面直接来看 SpringApplication#run(java.lang.String...)
方法的执行流程:
public ConfigurableApplicationContext run(String... args) {
// 开启关于启动时间的信息监控
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 准备 ApplicationContext
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
configureHeadlessProperty();
// 1. 获取Spring的监听器类,这里是从 spring.factories 中去获取,默认的是以 org.springframework.boot.SpringApplicationRunListener 为key,获取到的监听器类型为 EventPublishingRunListener。
SpringApplicationRunListeners listeners = getRunListeners(args);
// 1.1 监听器发送启动事件
listeners.starting();
try {
// 封装参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2. 构造容器环境。将容器的一些配置内容加载到 environment 中
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置BeanInfo的忽略 :“spring.beaninfo.ignore”,值为“true”表示跳过对BeanInfo类的搜索
configureIgnoreBeanInfo(environment);
// 打印信息对象
Banner printedBanner = printBanner(environment);
// 3. 创建上下文对象
context = createApplicationContext();
// 从 spring.factries 中获取错误报告的类。出错的时候会调用其方法通知
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 4. 准备刷新上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 5. 刷新上下文
refreshContext(context);
// 结束刷新,留待扩展功能,并未实现什么
afterRefresh(context, applicationArguments);
// 停止监听
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 监听器发送启动结束时间
listeners.started(context);
// 调用 ApplicationRunner 和 CommandLineRunner 对应的方法
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, except