
> 你是否想过, 为什么SpringBoot可以不用外置的Tomcat容器. 那么我下文带你了解为什么 ? 代码有些枯燥.
>
> 虽然SpringBoot的 `org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration` 可以实现自动注入, 难道你不想知道为什么吗 ?
我们从 tomcat 类往下走 .
org.apache.catalina.startup.Tomcat 这个就是一个tomcat实例 , 需要传入大量参数.
Minimal tomcat starter for embedding/unit tests.
用于嵌入/单元测试的最小tomcat启动器。 所以人家说了用于切入式和单元测试使用的 . 对于springboot也是基于这个的. 启动方式很简单,
public void start() throws LifecycleException {
getServer();
getConnector();
server.start();
}
我们往上走 org.springframework.boot.web.embedded.tomcat.TomcatWebServer 实现了org.springframework.boot.web.server.WebServer 接口, 可以看出还有其他切入式容器.
其 org.springframework.boot.web.embedded.tomcat.TomcatWebServer#initialize 方法会调用start方法.
private void initialize() throws WebServerException {
this.tomcat.start();
// .....
}
在这个对象实例化的时候就会被调用 org.springframework.boot.web.embedded.tomcat.TomcatWebServer#TomcatWebServer(org.apache.catalina.startup.Tomcat, boolean)
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
// 初始化tomcat
initialize();
}
继续往上走 org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getTomcatWebServer
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
继续往上走 org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1. 实例化 Tomcat
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
// 2. 设置basedir , 文件路径 , 默认在user用户下的.temp目录下. C:\Users\12986\AppData\Local\Temp\tomcat.4249223367467675192.8080
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 3. 初始化一个connector , 放入协议`org.apache.coyote.http11.Http11NioProtocol`
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
// 4. 设置connector
tomcat.setConnector(connector);
// 自动发布为false.其实就是热部署
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.servlet.context.ServletWebServerApplicationContext#createWebServer
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();
}
继续往上走 org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
继续往上走 org.springframework.context.support.AbstractApplicationContext#refresh 方法了 我们就先放到这里, 因为我们知道 spring的context在初始化完成会调用refresh刷新(也就是实例化和事件发布)机制.
所以我们回到了SpringApplication.run(SpringEmbedServletContainerApplication.class, args); 我们的入口
这个其实就是到头了 我们分析一下 SpringApplication
SpringApplication.run(SpringEmbedServletContainerApplication.class, args);
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
继续进入构造方法.
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 这里获取 webApplicationType
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
this.webApplicationType = deduceWebApplicationType(); 这里要获取webApplicationType , 所以进入了org.springframework.boot.SpringApplication#deduceWebApplicationType 来判断类型. 在没有任何配置的情况的下默认是 WebApplicationType.SERVLET 类型. 这个玩意是有用,
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 返回SERVLET
return WebApplicationType.SERVLET;
}
org.springframework.boot.SpringApplication#run(java.lang.String...) 启动了 . 这个是整个初始化过程.
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//1. 通过反射创建一个 context .
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//2. 这里要刷新 context
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
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;
}
我们看第一步. context = createApplicationContext(); -> org.springframework.boot.SpringApplication#createApplicationContext 这里了
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
// 显然我们是 SERVLET类型. 反射生成这个对象.
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
第二步会调用 refreshContext(context); -> org.springframework.boot.SpringApplication#refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
继续进入refresh(context); -> org.springframework.boot.SpringApplication#refresh
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
这里的这个context是 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 类型(在我们初始化的时候我们知道是Servlet类型,所以可以断定) , 他继承 了 org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
我们看他的 refresh方法org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#refresh
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
继续走 调用的是 super.refresh(); 父类是 org.springframework.context.support.AbstractApplicationContext#refresh ,就找到了 org.springframework.context.support.AbstractApplicationContext#onRefresh 方法调用了. 就和我们上面接入了, 所以 整个tomcat初始化过程就实现了 .
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 这里就是初始 tomcat容器了 .
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}