SpringBoot中Filter?bean如何添加到Servlet容器
對于Spring Boot的IOC容器——ServletWebServerApplicationContext,其中的Filter bean,每個Filter bean都會被獨立的注冊成為Servlet的Filter。大概的注冊過程分成2步:
- IOC容器——ServletWebServerApplicationContext將Filter接口的實現類封裝成FilterRegistrationBean,放到ServletContextInitializerBeans實例的成員變量initializers變量(LinkedMultiValueMap)中
- Spring 容器(ServletWebServerApplicationContext)從ServletContextInitializerBeans實例的成員變量initializers變量(LinkedMultiValueMap),中獲取所有的ServletContextInitializer實現類,調用它們的onStartUp函數,FilterRegistrationBean的onStartup函數就是在調用ServletContext的addFilter函數向Servlet添加Filter
1. IOC容器——ServletWebServerApplicationContext將Filter接口的實現類封裝成FilterRegistrationBean,放到ServletContextInitializerBeans實例的成員變量initializers變量(LinkedMultiValueMap)中
1.Spring容器——ServletWebServerApplicationContext的refresh()函數被調用。
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}2.ServletWebServerApplicationContext的createWebServer()函數,
- 在函數中會調用getSelfInitializer()函數獲取所有的ServletContextInitializer實例,
- 而獲取所有的ServletContextInitializer實例的過程中,會查找所有實現了Filter接口的bean,并注冊成FilterRegistrationBean(實現了ServletContextInitializer接口,所以也屬于ServletContextInitializer實例),
- 然后把FilterRegistrationBean放到initializers變量(LinkedMultiValueMap)中
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
// getSelfInitializer()函數中獲取所有ServletContextInitializer實例,調用onStartup方法完成注冊
this.webServer = factory.getWebServer(getSelfInitializer());
......
}
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);
// 獲取所有ServletContextInitializer實例,調用onStartup方法
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
// 傳入BeanFactory,new 一個ServletContextInitializerBeans對象,這個對象實現了Collection接口,是一個集合,
// 集合內的元素是需要內置在Servlet Context上的ServletContextInitializer bean
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}3.ServletContextInitializerBeans的構建
public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 找直接定義為ServletContextInitializer的bean(比如FilterRegistrationBean),DelegatingFilterProxyRegistrationBean等,
// 添加到initializers中
addServletContextInitializerBeans(beanFactory);
// 找實現了Filter接口的bean,將他們封裝成ServletContextInitializer bean(對于Filter的實現類也就是封裝成FilterRegistrationBean),
// 添加到initializers中
addAdaptableBeans(beanFactory);
//排序
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
// 獲取實現了Servlet接口的bean
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
// 查找所有實現了Filter接口的bean,并注冊成FilterRegistrationBean,
// 然后把FilterRegistrationBean放到initializers變量(LinkedMultiValueMap)中
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}所以我們可以看到ServletContextInitializerBeans構造函數是怎么構造一個包含
ServletContextInitializer bean集合的:
- 找直接定義為ServletContextInitializer的bean(比如FilterRegistrationBean),
- DelegatingFilterProxyRegistrationBean等,添加到initializers中
找實現了Filter接口的bean,將他們封裝成ServletContextInitializer bean(對于Filter的實現類也就是封裝成FilterRegistrationBean),添加到initializers中 - 排序然后一并返回
所以ServletContextInitializerBeans實例表示的是:一個從ListableBeanFactory bean容器中獲得的ServletContextInitializer實例的集合。這個集合中的每個元素來自容器中定義的每個如下類型的bean :
- 實現了ServletContextInitializer接口的bean
- 具體可能以ServletRegistrationBean/FilterRegistrationBean/EventListenerRegistrationBean的形式存在。這些 bean 設計的目的是用來注冊相應的 Servlet/Filter/EventListener bean 到 ServletContext
- **實現了Servlet/Filter/EventListener接口的 bean**
這些 bean直接以實現裸的Servlet/Filter/EventListener接口的 bean的形式存在,但是Springboot會將它們封裝成相應的 RegistrationBean(也是ServletContextInitializer ),然后也注冊到ServletContext。
也就是說,Spring幫我們將實現裸的Servlet/Filter/EventListener接口的 bean,封裝成ServletRegistrationBean/FilterRegistrationBean/EventListenerRegistrationBean。
2. Spring 容器(ServletWebServerApplicationContext)從ServletContextInitializerBeans實例的成員變量initializers變量(LinkedMultiValueMap),中獲取所有的ServletContextInitializer實現類,調用它們的startUp函數,FilterRegistrationBean的onStartup函數就是在調用ServletContext的addFilter函數向Servlet添加Filter
1.還是要回到上面1.2中的createWebServer()函數中,這次我們不看getSelfInitializer(),而是看factory.getWebServer(...)函數。
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
// getSelfInitializer()函數中獲取所有ServletContextInitializer實例,調用onStartup方法完成注冊
this.webServer = factory.getWebServer(getSelfInitializer());
......
}2.TomcatServletWebServerFactory的getWebServer(...)函數
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
......
// 將前面獲取到的所有ServletContextInitializer實例作為參數,傳下去
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}3.TomcatServletWebServerFactory的prepareContext(...)
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
......
// 合并一下
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
// 將前面獲取到的所有ServletContextInitializer實例作為參數,傳下去
configureContext(context, initializersToUse);
postProcessContext(context);}4.tomcatServletWebServerFactory的configureContext(...),創(chuàng)建了TomcatStarter,并將TomcatStarter綁定到Tomcat上,這樣Tomcat啟動時就會回調TomcatStarter的onStartup函數。
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 將前面獲取到的所有ServletContextInitializer實例作為參數,傳下去
TomcatStarter starter = new TomcatStarter(initializers);
// 下面這一系列操作是將TomcatStarter綁定到Tomcat上,這樣TomcatStarter就會在Tomcat啟動時被回調
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);
......
}5.TomcatStarter的startUp函數被調用的時候,就會變量所有的ServletContextInitializer實例的onStartup函數,
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());
}
}
}6.回顧第一章:“Filter接口的實現類封裝成FilterRegistrationBean”,而FilterRegistrationBean又是ServletContextInitializer接口的實現類,所以上面第5步,也會回調FilterRegistrationBean的onStartup函數,
//FilterRegistrationBean
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); return;
}
// 這里
register(description, servletContext);
}7.DynamicRegistrationBean.class
protected final void register(String description, ServletContext servletContext) {
//這里
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}8.AbstractFilterRegistrationBean.class, 看到沒,最終還是用ServletContext的addFilter函數,向Servlet容器中添加Filter。
protected Dynamic addRegistration(String description, ServletContext servletContext) {
// getFilter()在FilterRegistrationBean中被實現,返回的就是那個Filter接口的實現類。
Filter filter = getFilter();
// 將Filter接口的實現類添加到Servlet容器中
return servletContext.addFilter(getOrDeduceName(filter), filter);
}到此這篇關于SpringBoot中Filter bean是怎么被添加到Servlet容器中的的文章就介紹到這了,更多相關SpringBoot Filter bean添加到Servlet容器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SSH框架網上商城項目第8戰(zhàn)之查詢和刪除商品類別功能實現
SSH框架網上商城項目第8戰(zhàn)之查詢和刪除商品類別功能實現,為項目增加功能,添加、更新、刪除和查詢操作,感興趣的小伙伴們可以參考一下2016-05-05
IDEA項目maven?project沒有出現plugins和Dependencies問題
這篇文章主要介紹了IDEA項目maven?project沒有出現plugins和Dependencies問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
IDEA使用jformdesigner插件做管理系統MVC架構的步驟和實現思路
在?IntelliJ?IDEA?中結合?JFormDesigner?插件,通過?Swing?框架實現一個管理系統的?MVC?架構是一種經典的開發(fā)方式,以下是具體的步驟和實現思路,包含從項目創(chuàng)建到?MVC?架構的核心代碼實現,需要的朋友可以參考下2024-12-12
詳解Java中NullPointerException的處理方法
這篇文章將帶大家來單獨看一個很常見的異常--空指針異常,這個可以說是每個Java程序員都必知的異常,所以我們不得不單獨學習一下,文中有詳細的代碼示例,需要的朋友可以參考下2023-08-08
springboot?publish?event?事件機制demo分享
這篇文章主要介紹了springboot?publish?event?事件機制demo,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10

