SpringBoot中Filter?bean如何添加到Servlet容器
對(duì)于Spring Boot的IOC容器——ServletWebServerApplicationContext,其中的Filter bean,每個(gè)Filter bean都會(huì)被獨(dú)立的注冊(cè)成為Servlet的Filter。大概的注冊(cè)過(guò)程分成2步:
- IOC容器——ServletWebServerApplicationContext將Filter接口的實(shí)現(xiàn)類封裝成FilterRegistrationBean,放到ServletContextInitializerBeans實(shí)例的成員變量initializers變量(LinkedMultiValueMap)中
- Spring 容器(ServletWebServerApplicationContext)從ServletContextInitializerBeans實(shí)例的成員變量initializers變量(LinkedMultiValueMap),中獲取所有的ServletContextInitializer實(shí)現(xiàn)類,調(diào)用它們的onStartUp函數(shù),F(xiàn)ilterRegistrationBean的onStartup函數(shù)就是在調(diào)用ServletContext的addFilter函數(shù)向Servlet添加Filter
1. IOC容器——ServletWebServerApplicationContext將Filter接口的實(shí)現(xiàn)類封裝成FilterRegistrationBean,放到ServletContextInitializerBeans實(shí)例的成員變量initializers變量(LinkedMultiValueMap)中
1.Spring容器——ServletWebServerApplicationContext的refresh()函數(shù)被調(diào)用。
protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
2.ServletWebServerApplicationContext的createWebServer()函數(shù),
- 在函數(shù)中會(huì)調(diào)用getSelfInitializer()函數(shù)獲取所有的ServletContextInitializer實(shí)例,
- 而獲取所有的ServletContextInitializer實(shí)例的過(guò)程中,會(huì)查找所有實(shí)現(xiàn)了Filter接口的bean,并注冊(cè)成FilterRegistrationBean(實(shí)現(xiàn)了ServletContextInitializer接口,所以也屬于ServletContextInitializer實(shí)例),
- 然后把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()函數(shù)中獲取所有ServletContextInitializer實(shí)例,調(diào)用onStartup方法完成注冊(cè) 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實(shí)例,調(diào)用onStartup方法 for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } } // 傳入BeanFactory,new 一個(gè)ServletContextInitializerBeans對(duì)象,這個(gè)對(duì)象實(shí)現(xiàn)了Collection接口,是一個(gè)集合, // 集合內(nèi)的元素是需要內(nèi)置在Servlet Context上的ServletContextInitializer bean protected Collection<ServletContextInitializer> getServletContextInitializerBeans() { return new ServletContextInitializerBeans(getBeanFactory()); }
3.ServletContextInitializerBeans的構(gòu)建
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); // 找實(shí)現(xiàn)了Filter接口的bean,將他們封裝成ServletContextInitializer bean(對(duì)于Filter的實(shí)現(xiàn)類也就是封裝成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); // 獲取實(shí)現(xiàn)了Servlet接口的bean addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig)); // 查找所有實(shí)現(xiàn)了Filter接口的bean,并注冊(cè)成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構(gòu)造函數(shù)是怎么構(gòu)造一個(gè)包含
ServletContextInitializer bean集合的:
- 找直接定義為ServletContextInitializer的bean(比如FilterRegistrationBean),
- DelegatingFilterProxyRegistrationBean等,添加到initializers中
找實(shí)現(xiàn)了Filter接口的bean,將他們封裝成ServletContextInitializer bean(對(duì)于Filter的實(shí)現(xiàn)類也就是封裝成FilterRegistrationBean),添加到initializers中 - 排序然后一并返回
所以ServletContextInitializerBeans實(shí)例表示的是:一個(gè)從ListableBeanFactory bean容器中獲得的ServletContextInitializer實(shí)例的集合。這個(gè)集合中的每個(gè)元素來(lái)自容器中定義的每個(gè)如下類型的bean :
- 實(shí)現(xiàn)了ServletContextInitializer接口的bean
- 具體可能以ServletRegistrationBean/FilterRegistrationBean/EventListenerRegistrationBean的形式存在。這些 bean 設(shè)計(jì)的目的是用來(lái)注冊(cè)相應(yīng)的 Servlet/Filter/EventListener bean 到 ServletContext
- **實(shí)現(xiàn)了Servlet/Filter/EventListener接口的 bean**
這些 bean直接以實(shí)現(xiàn)裸的Servlet/Filter/EventListener接口的 bean的形式存在,但是Springboot會(huì)將它們封裝成相應(yīng)的 RegistrationBean(也是ServletContextInitializer ),然后也注冊(cè)到ServletContext。
也就是說(shuō),Spring幫我們將實(shí)現(xiàn)裸的Servlet/Filter/EventListener接口的 bean,封裝成ServletRegistrationBean/FilterRegistrationBean/EventListenerRegistrationBean。
2. Spring 容器(ServletWebServerApplicationContext)從ServletContextInitializerBeans實(shí)例的成員變量initializers變量(LinkedMultiValueMap),中獲取所有的ServletContextInitializer實(shí)現(xiàn)類,調(diào)用它們的startUp函數(shù),F(xiàn)ilterRegistrationBean的onStartup函數(shù)就是在調(diào)用ServletContext的addFilter函數(shù)向Servlet添加Filter
1.還是要回到上面1.2中的createWebServer()函數(shù)中,這次我們不看getSelfInitializer(),而是看factory.getWebServer(...)函數(shù)。
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()函數(shù)中獲取所有ServletContextInitializer實(shí)例,調(diào)用onStartup方法完成注冊(cè) this.webServer = factory.getWebServer(getSelfInitializer()); ...... }
2.TomcatServletWebServerFactory的getWebServer(...)函數(shù)
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } ...... // 將前面獲取到的所有ServletContextInitializer實(shí)例作為參數(shù),傳下去 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實(shí)例作為參數(shù),傳下去 configureContext(context, initializersToUse); postProcessContext(context);}
4.tomcatServletWebServerFactory的configureContext(...),創(chuàng)建了TomcatStarter,并將TomcatStarter綁定到Tomcat上,這樣Tomcat啟動(dòng)時(shí)就會(huì)回調(diào)TomcatStarter的onStartup函數(shù)。
protected void configureContext(Context context, ServletContextInitializer[] initializers) { // 將前面獲取到的所有ServletContextInitializer實(shí)例作為參數(shù),傳下去 TomcatStarter starter = new TomcatStarter(initializers); // 下面這一系列操作是將TomcatStarter綁定到Tomcat上,這樣TomcatStarter就會(huì)在Tomcat啟動(dòng)時(shí)被回調(diào) if (context instanceof TomcatEmbeddedContext) { TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context; embeddedContext.setStarter(starter); embeddedContext.setFailCtxIfServletStartFails(true); } context.addServletContainerInitializer(starter, NO_CLASSES); ...... }
5.TomcatStarter的startUp函數(shù)被調(diào)用的時(shí)候,就會(huì)變量所有的ServletContextInitializer實(shí)例的onStartup函數(shù),
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接口的實(shí)現(xiàn)類封裝成FilterRegistrationBean”,而FilterRegistrationBean又是ServletContextInitializer接口的實(shí)現(xiàn)類,所以上面第5步,也會(huì)回調(diào)FilterRegistrationBean的onStartup函數(shù),
//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, 看到?jīng)],最終還是用ServletContext的addFilter函數(shù),向Servlet容器中添加Filter。
protected Dynamic addRegistration(String description, ServletContext servletContext) { // getFilter()在FilterRegistrationBean中被實(shí)現(xiàn),返回的就是那個(gè)Filter接口的實(shí)現(xiàn)類。 Filter filter = getFilter(); // 將Filter接口的實(shí)現(xiàn)類添加到Servlet容器中 return servletContext.addFilter(getOrDeduceName(filter), filter); }
到此這篇關(guān)于SpringBoot中Filter bean是怎么被添加到Servlet容器中的的文章就介紹到這了,更多相關(guān)SpringBoot Filter bean添加到Servlet容器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SSH框架網(wǎng)上商城項(xiàng)目第8戰(zhàn)之查詢和刪除商品類別功能實(shí)現(xiàn)
SSH框架網(wǎng)上商城項(xiàng)目第8戰(zhàn)之查詢和刪除商品類別功能實(shí)現(xiàn),為項(xiàng)目增加功能,添加、更新、刪除和查詢操作,感興趣的小伙伴們可以參考一下2016-05-05IDEA項(xiàng)目maven?project沒(méi)有出現(xiàn)plugins和Dependencies問(wèn)題
這篇文章主要介紹了IDEA項(xiàng)目maven?project沒(méi)有出現(xiàn)plugins和Dependencies問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12IDEA使用jformdesigner插件做管理系統(tǒng)MVC架構(gòu)的步驟和實(shí)現(xiàn)思路
在?IntelliJ?IDEA?中結(jié)合?JFormDesigner?插件,通過(guò)?Swing?框架實(shí)現(xiàn)一個(gè)管理系統(tǒng)的?MVC?架構(gòu)是一種經(jīng)典的開(kāi)發(fā)方式,以下是具體的步驟和實(shí)現(xiàn)思路,包含從項(xiàng)目創(chuàng)建到?MVC?架構(gòu)的核心代碼實(shí)現(xiàn),需要的朋友可以參考下2024-12-12詳解Java中NullPointerException的處理方法
這篇文章將帶大家來(lái)單獨(dú)看一個(gè)很常見(jiàn)的異常--空指針異常,這個(gè)可以說(shuō)是每個(gè)Java程序員都必知的異常,所以我們不得不單獨(dú)學(xué)習(xí)一下,文中有詳細(xì)的代碼示例,需要的朋友可以參考下2023-08-08springboot?publish?event?事件機(jī)制demo分享
這篇文章主要介紹了springboot?publish?event?事件機(jī)制demo,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10解決Mybatis-Plus操作分頁(yè)后數(shù)據(jù)失效問(wèn)題
這篇文章主要介紹了解決Mybatis-Plus操作分頁(yè)后數(shù)據(jù)失效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解
這篇文章主要介紹了Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解,本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09java異步寫(xiě)日志到文件中實(shí)現(xiàn)代碼
這篇文章主要介紹了java異步寫(xiě)日志到文件中實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04