Spring Boot Filter,Servlet,Listener的加载过程
这里在说明这个过程之前需要先列出来几个比较重要的类
- ServletContainerInitializer 这是一个关键接口,任何程序可以通过该接口向Tomcat添加Filter,Servlet,Listener
- ServletContextInitializer 这个接口是Spring中定义的可以看到它和ServletContainerInitializer 接口几乎完全一样,也就是说在Spring Boot中实现ServletContextInitializer 就可以把对应的Filter,Servlet,Listener 配置到Tomcat,而Spring Boot只有一个ServletContextInitializer 的实现类(TomcatStarter)这样就保证了对外的接口尽量单一,降低了耦合度。而Filter,Servlet,Listener对应的RegistrationBean都实现了ServletContextInitializer 接口
- ServletWebServerApplicationContext 这个类是Servlet应用的上下文类,而Spring Boot默认使用的是它的子类 AnnotationConfigServletWebServerApplicationContext,这个上下文管理了所有的Spring 容器中的类
- TomcatStarter 该类实现了ServletContainerInitializer 并且持有容器中的所有ServletContextInitializer实现类的实例,向Tomcat中注入Filter,Servlet,Listener都是通过该类完成的
那么好了有了以上的几个类那Spring是怎么把这几个类联合使用的呢?
Spring Boot 和Tomcat的纽带
ServletWebServerApplicationContext 这个上下文类同时持有WebServer实例(也就是Tomcat实现),和所有的ServletContextInitializer 实现类,并在创建webServer实例时将这些ServletContextInitializer 的实现类注入到了TomcatStarter 类中
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//创建 WebServer并把getSelfInitializer 注入到了TomcatStarter中
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();
}
再来看一下getSelfInitializer()的实现
/**
这个方法只是调用了一个私有方法来做ServletContextInitializer的实现
*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
servletContext);
//getServletContextInitializerBeans() 会将所有实现了ServletContextInitializer 接口的类全部从Spring容器中加载出来
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
getWebServer的实现
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new 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);
}
//配置Tomcat的上下文
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
prepareContext 方法的定义
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
//实例化Tomcat上下文类
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
File docBase = (documentRoot != null) ? documentRoot
: createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader(
(this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
context.setUseRelativeRedirects(false);
configureTldSkipPatterns(context);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
context.addLifecycleListener(new StaticResourceConfigurer(context));
//加载到所有的ServletContextInitializer实例
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
//将配置写入到Tomcat上下文
configureContext(context, initializersToUse);
postProcessContext(context);
}
configureContext 方法的定义
protected void configureContext(Context context,
ServletContextInitializer[] initializers) {
//创建TomcatStarter
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
//将TomcatStarter加入到上下文
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
for (Valve valve : this.contextValves) {
context.getPipeline().addValve(valve);
}
for (ErrorPage errorPage : getErrorPages()) {
new TomcatErrorPage(errorPage).addToContext(context);
}
for (MimeMappings.Mapping mapping : getMimeMappings()) {
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
configureSession(context);
new DisableReferenceClearingContextCustomizer().customize(context);
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
Spring boot中添加Filter,Servlet,Listener
通过上面的代码分析可知Spring Boot 会把容器中所有实现了ServletContextInitializer的实例加载出来并尝试将它们注册到Tomcat中而 RegistrationBean就实现了ServletContextInitializer并且该类为Filter,Servlet,Listener对应RegistrationBean的父类所以我们只需要把对应的RegistrationBean 加入到Spring容器就可以实现向Tomcat中注册对应的Filter,Servlet,Listener。