ServletContextInitializer添加 servlet filter listener
https://www.cnblogs.com/pomer-huang/p/9639322.html
关于springboot中添加Filter的方法
https://www.jianshu.com/p/3d421fbce734
———————————————————————————————————————————————————————————————
Spring Boot Servlet : ServletContextInitializer
概述
功能介绍
Spring Boot提供的在Servlet 3.0+环境中用于程序化配置ServletContext的接口。该接口ServletContextInitializer主要被RegistrationBean实现用于往ServletContext容器中注册Servlet,Filter或者EventListener。这些ServletContextInitializer的设计目的主要是用于这些实例被Spring IoC容器管理。这些ServletContextInitializer实例不会被SpringServletContainerInitializer检测,因此不会被Servlet容器自动启动。
该接口
ServletContextInitializer和Spring Web的另外一个接口WebApplicationInitializer看起来几乎一模一样。但二者使用目的不同。Spring Web中,WebApplicationInitializer也是针对Servlet 3.0+环境,设计用于程序化配置ServletContext,跟传统的web.xml相对或者配合使用。WebApplicationInitializer实现类会被SpringServletContainerInitializer自动检测和启动。
继承关系

使用
关于ServletContextInitializer的应用可以参考下面的例子。TomcatStarter是Spring Boot Web Servlet应用结合Tomcat使用时的一个ServletContainerInitializer实现。从下面代码不难看出,一组ServletContextInitializer会被设置到ServletContainerInitializer TomcatStarter上,而TomcatStarter在Servlet容器启动过程中调用自己的方法#onStartup,会逐一调用这些ServletContextInitializer的方法#onStartup初始化ServletContext。
package org.springframework.boot.web.embedded.tomcat;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
class TomcatStarter implements ServletContainerInitializer {
private static final Log logger = LogFactory.getLog(TomcatStarter.class);
// 使用者会指定一组 ServletContextInitializer 给 TomcatStarter
private final ServletContextInitializer[] initializers;
private volatile Exception startUpException;
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
// Servlet 容器启动时回会用该方法,该方法会逐一调用每个 ServletContextInitializer 的方法
// #onStartup 会指定 ServletContext 进行初始化。这些 ServletContextInitializer 的目的
// 通常会是 注册 Servlet, Filter 或者 EventListener 。
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
// Prevent Tomcat from logging and re-throwing when we know we can
// deal with it in the main thread, but log for information here.
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: "
+ ex.getClass().getName() + ". Message: " + ex.getMessage());
}
}
}
public Exception getStartUpException() {
return this.startUpException;
}
}
源代码
源代码版本 : 2.1.3.RELEASE
package org.springframework.boot.web.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.SpringServletContainerInitializer;
import org.springframework.web.WebApplicationInitializer;
@FunctionalInterface
public interface ServletContextInitializer {
/**
* Configure the given ServletContext with any servlets, filters, listeners
* context-params and attributes necessary for initialization.
* @param servletContext the ServletContext to initialize
* @throws ServletException if any call against the given ServletContext
* throws a ServletException
*/
void onStartup(ServletContext servletContext) throws ServletException;
}
参考文章
——————————————————————————————————————————————————————————————————
springboot 2.1.3.RELEASE添加filter,servlet源码学习
Servlet规范中,通过ServeltContext来注册Filter、Servlet,这里分析Filter,Servlet是相同逻辑
springboot2.0中,我们通过
FilterRegistrationBean将指定得filter来实现ServeltContext注册filter
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
FilterRegistrationBean的实例化过程public FilterRegistrationBean(T filter, ServletRegistrationBean... servletRegistrationBeans) { super(servletRegistrationBeans); Assert.notNull(filter, "Filter must not be null"); this.filter = filter;}super的实例化AbstractFilterRegistrationBean(ServletRegistrationBean... servletRegistrationBeans) { Assert.notNull(servletRegistrationBeans, "ServletRegistrationBeans must not be null"); Collections.addAll(this.servletRegistrationBeans, servletRegistrationBeans);} |
可知FilterRegistrationBean得实例化过程就是将Filter保存到servletRegistrationBeans(一个set)中
再分析FilterRegistrationBean类

FilterRegistrationBean的父类是一个ServletContextInitializer,他有一个方法onStartup(ServletContext servletContext)
其结果最终会调用
servletContext.addFilter(this.getOrDeduceName(filter), filter)
现在看看ServletContextInitializer.onStartup的调用地方
|
1
2
3
4
5
6
7
8
9
10
11
12
|
springboot启动new SpringApplication(XXApplication.class).run(XXApplication.class,args)调用refreshContext(context); -> refresh(context); -> ((AbstractApplicationContext) applicationContext).refresh();-> ServletWebServerApplicationContext.onRefresh(); -> createWebServer();WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer());} |
再分析getSelfInitializer方法
|
1
2
3
4
|
prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),servletContext); for (ServletContextInitializer beans :getServletContextInitializerBeans()) { //调用ServletContextInitializer的onStartup方法 |
|
1
|
beans.onStartup(servletContext); }-> getServletContextInitializerBeans() |
|
1
2
|
-> new ServletContextInitializerBeans(getBeanFactory())-> addAdaptableBeans(beanFactory) |
|
1
2
3
4
5
6
7
8
|
addAdaptableBeans(ListableBeanFactory beanFactory) 代码如下:MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);//将servlet类的bean包装为ServletRegistrationBeanaddAsRegistrationBean(beanFactory, Servlet.class,new ServletRegistrationBeanAdapter(multipartConfig));//将Filter的bean包装为FilterRegistrationBeanaddAsRegistrationBean(beanFactory, Filter.class,new FilterRegistrationBeanAdapter());for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) { addAsRegistrationBean(beanFactory, EventListener.class,(Class<EventListener>) listenerType,new ServletListenerRegistrationBeanAdapter());} |
这里提一下,如果Servlet的bean实例名为dispatcherServlet,且该实例在seen集合中(seen集合为自定义配置的实例,通过ServletContextInitializerBeans.addServletContextInitializerBean入口可以查看源码),则设置默认值为 /
|
1
2
3
4
5
6
7
|
String url = (totalNumberOfSourceBeans != 1) ? "/" + name + "/" : "/";if (name.equals(DISPATCHER_SERVLET_NAME)) { url = "/"; // always map the main dispatcherServlet to "/"}ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(source,url); bean.setName(name);bean.setMultipartConfig(this.multipartConfig);return bean; |
总结:springboot启动时,会将所有的 FilterRegistrationBean、ServletRegistrationBean以及被spring管理的Servlet和Filter的实例,通过 ServletContext注册Servlet以及Filter