- 總結1:
- SCI:Servlet容器(Tomcat)提供的初始化Servlet容器本身的接口,可替換web.xml
- SpringSCI:SpringServletContainerInitializer,Srping提供的SCI的一個實現,起到中轉橋接作用,橋接到 WebApplicationInitializer 接口上
- WebApplicationInitializer :可以自定義配置servlet、listener,進而可以通過代碼手動控制兩個上下文的創建過程(參考:基於xml 參考:支持注解);只能創建上線文但不能配置上下文詳細內容,但可以指定上下文配置文件的路徑或配置類的類路徑
AbstractAnnotationConfigDispatcherServletInitializer
:將兩個上下文自動已創建好,已創建好的上下文的類型都是支持注解的;手動繼承后只需要指定兩個上下文配置類路徑即可。- WebMvcConfigurer:
WebMvcConfigurationAdapter已經廢棄,最好用implements WebMvcConfigurer代替
@Configuration public class MyConfig implements WebMvcConfigurer { }
如果使用繼承,WebMvcConfigurationSupport,DelegatingWebMvcConfiguration,或者使用@EnableWebMvc -
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own
@Configuration
class of typeWebMvcConfigurer
but without@EnableWebMvc
. If you wish to provide custom instances ofRequestMappingHandlerMapping
,RequestMappingHandlerAdapter
, orExceptionHandlerExceptionResolver
, you can declare aWebMvcRegistrationsAdapter
instance to provide such components.If you want to take complete control of Spring MVC, you can add your own
@Configuration
annotated with@EnableWebMvc
.
- 總結2:
- java提供SPI,用於加載指定接口的 特定實現類
- servlet容器提供SCI接口,用於配置servlet容器初始化;這個過程使用了java的SPI機制;類似Logfactory的工作原理
- Spring提供SpringServletContainerInitializer類(該類實現類SCI接口),該類簡稱SpringSCI,該類不直接配置servlet容器,而是通過查找WebApplicationInitializer接口(用於web初始化)的實現類,間接配置servlet容器;
- Spring MVC提供基類 AbstractAnnotationConfigDispatcherServletInitializer,用於DispatcherServlet初始化(實現了WebApplicationInitializer接口),該基類既要完成WebApplicationInitializer接口中配置servlet容器的功能,又完成了配置MVC的功能,即同時配置了DispatcherServlet 和 ContextLoaderListener
- Spring提供的接口
WebApplicationInitializer,用於配置servlet容器,並不是只用於配置MVC
,可通過自定義二次開發繼承WebApplicationInitializer,結合ServletRegistration.Dynamic
,配置servlet容器,添加自定義的servlet、filter等,可完全替換掉web.xml - 如果是在MVC項目中,想添加自定義的servlet、filter,可不用實現
WebApplicationInitializer
,直接只要重寫AbstractAnnotationConfigDispatcherServletInitializer
類的getServletFilters()
方法就行了;不需要 mapping,因為會自動 mapping 到DispatcherServlet
上,通過返回多個 filter,可以添加多個 filter。
- 總結3:spring 依據“java的SPI(Service Provider Interface)機制”和“servlet容器的SCI(ServletContainerInitializer)接口”,通過SpringServletContainerInitializer實現spring組件和servlet容器解耦,解耦后,spring組件可以直接使用spring提供的WebApplicationInitializer接口,實現類似SCI功能,通過實現WebApplicationInitializer,可以向servlet容器添加servlet,listener等。
- Servlet 3.0 規范和 Spring DispatcherServlet 配置
在 Servlet 3.0 的環境中,容器會在 classpath 中尋找繼承了 javax.servlet.ServletContainerInitializer 接口的類,用它來配置 servlet 容器。
Spring 提供了一個繼承這個接口的類 SpringServletContainerInitializer,在這個類中,它會尋找任何繼承了 WebApplicationInitializer 接口的類並用其來配置 servlet 容器。 - Spring 3.2 提供了一個繼承了 WebApplicationInitializer 接口的基類 AbstractAnnotationConfigDispatcherServletInitializer,用於配置Spring MVC項目。Spring 3.2 開始引入一個簡易的 WebApplicationInitializer 實現類,這就是 AbstractAnnotationConfigDispatcherServletInitializer。
-
在AbstractAnnotationConfigDispatcherServletInitializer中,給我們留下了三個抽象方法要求我們去實現:
- protected abstract String[] getServletMappings(); 這個方法在AbstractDispatcherServletInitializer 中,這個方法可以將一個或多個路徑映射到DispatcherServlet上,如果路徑設置為“/”,則所有的請求都會由DispatcherServlet處理。
- protected abstract Class<?>[] getRootConfigClasses(); 這兩個方法在AbstractAnnotationConfigDispatcherServletInitializer中
- protected abstract Class<?>[] getServletConfigClasses();
- 在Spring MVC項目中
- 你的 servlet 配置類只需要繼承 AbstractAnnotationConfigDispatcherServletInitializer,就會被發現而用於 servlet 容器的配置。
- AbstractAnnotationConfigDispatcherServletInitializer用於創建兩種應用上下文:
DispatcherServlet
創建的和攔截器ContextLoaderListener
創建的上下文 -
在 AbstractAnnotationConfigDispatcherServletInitializer 中 DispatcherServlet 和 ContextLoaderListener 都會被創建,而基類中的方法就可用來創建不同的應用上下文:
-
getServletConfigClasses():定義 DispatcherServlet 應用上下文中的 beans;
- getRootConfigClasses():定義攔截器 ContextLoaderListener 應用上下文中的 beans
-
- 示例
-
package spittr.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import spittr.web.WebConfig; public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
-
- 配置額外的 servlets 和 filters(非Bean,非DispatcherServlet 和 ContextLoaderListener中的元素)
- 如果需要定義額外的 servlets 或 filters,只需要創建額外的初始化類。在 Spring MVC 中可以通過繼承
WebApplicationInitializer
接口來實現。 -
定義一個新的 servlet: package com.myapp.config; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration.Dynamic; import org.springframework.web.WebApplicationInitializer; import com.myapp.MyServlet; public class MyServletInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext throws ServletException { Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class); myServlet.addMapping("/custom/**"); } } 定義 filters 和 listeners: @Override public void onStartup(ServletContext servletContext) throws ServletException { // 定義filter javax.servlet.FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", MyFilter.class); filter.addMappingForUrlPatterns(null, false, "/custom/*"); }
-
如果你需要為 DispatcherServlet 添加 filter 的話,就不用這么麻煩了,你只要重寫 AbstractAnnotationConfigDispatcherServletInitializer 類的 getServletFilters() 方法就行了:
@Override protected Filter[] getServletFilters() { return new Filter[] { new MyFilter() }; }
不需要 mapping,因為會自動 mapping 到 DispatcherServlet 上,通過返回多個 filter,可以添加多個 filter
- 如果需要定義額外的 servlets 或 filters,只需要創建額外的初始化類。在 Spring MVC 中可以通過繼承
- 參考:https://blog.csdn.net/w1196726224/article/details/52687324
- 參考:[Servlet3.0研究之ServletContainerInitializer接口 - 夫禮者的專欄 - CSDN博客: https://blog.csdn.net/lqzkcx3/article/details/78507169]
- 參考:[java中的SPI機制 - 司剛軍的個人專欄 - CSDN博客: https://blog.csdn.net/sigangjun/article/details/79071850]
- 參考:[JavaSPI機制學習筆記 - 琴水玉 - 博客園: http://www.cnblogs.com/lovesqcc/p/5229353.html]
- 參考:[Servlet3.0研究之ServletContainerInitializer接口 - 夫禮者的專欄 - CSDN博客: https://blog.csdn.net/lqzkcx3/article/details/78507169]
- Service Provider Interface:
java spi的具體使用如下 :
當服務的提供者,提供了服務接口的一種實現之后,在jar包的META-INF/services/目錄里同時創建一個以服務接口命名的文件。該文件里就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/里的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。
基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼里制定。
jdk提供服務實現查找的一個工具類:java.util.ServiceLoader
- Servlet 3.0 規范和 Spring DispatcherServlet 配置
- 使用JavaConfig普通web應用(非MVC)
-
public class App { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext( AppConfig.class); CustomerBo customer = (CustomerBo) context.getBean("customer"); customer.printMsg("Hello 1"); SchedulerBo scheduler = (SchedulerBo) context.getBean("scheduler"); scheduler.printMsg("Hello 2"); } } ############## import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import({ CustomerConfig.class, SchedulerConfig.class }) public class AppConfig { } ############## @Configuration public class CustomerConfig { @Bean(name="customer") public CustomerBo customerBo(){ return new CustomerBo(); } } ############## public class SchedulerBo { public void printMsg(String msg) { System.out.println("SchedulerBo : " + msg); } }
-