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包装为ServletRegistrationBean
addAsRegistrationBean(beanFactory, Servlet.
class
,
new
ServletRegistrationBeanAdapter(multipartConfig));
//将Filter的bean包装为FilterRegistrationBean
addAsRegistrationBean(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