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