Tomcat 如何嵌入 SpringBoot
2018-04-05 本文已影响35人
莫那一鲁道
在启动 run 方法中,最重要的就是创建 Context,代码如下:
// 默认环境
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
org.springframework.boot.context.embedded.EmbeddedWebApplicationContext
重写了著名的 AbstractApplicationContext.refresh()
中的 onRefresh
方法,该方法内部代码如下:
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 启动嵌入的 默认 Tomcat 容器,支持 Jetty,Tomcat,Undertow
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
在 createEmbeddedServletContainer
方法中,会加载 一个 ServletContextInitializer
,,而这个类的 onStartup
方法的参数 ServletContext
就是包装了 Spring
AnnotationConfigEmbeddedWebApplicationContext
的 ApplicationContext
:
// AnnotationConfigEmbeddedWebApplicationContext 的父类 EmbeddedWebApplicationContext 的内部类:
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 从这里开始注入 Spring bean,
selfInitialize(servletContext);
}
};
}
这个 ServletContextInitializer 会被放入到 Tomcat 的 StandardContext 类的一个 initializers Map 中。在 Tomcat 启动 Servlet 的时候,也就是 StandardContext 调用 start 方法的时候,会循环调用 Map 中的 ServletContextInitializer 的 onStartup 方法,该方法就是开始启动 Spring 容器 getBean 方法的地方。
总结整个过程就是:
- SpringBoot 通过 run 方法创建了 Context。
- Context 在 onRefresh 方法中启动 Tomcat.
- Tomcat 启动时将先前创建的 SpringContext 设置到 Tomcat 中。然后开始了 SpringIOC 的过程。