META-INF\services\javax.servlet.ServletContainerInitializer
web容器啓動的時候,會掃描每個jar包中META-INF\services\javax.servlet.ServletContainerInitializer文件, 加載其中指定的ServletContainerInitializer的實現類,並調用onStartup方法。@HandlesTypes(value = {HelloServlet.class})
用於指定一個接口,所有該接口的實現類會傳給onStartup
方法的第一個參數。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
package com.example.demo3
// HelloServlet是個接口,我例子中的實現類就不貼出來了
@HandlesTypes(value = {HelloServlet.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 應用啓動的時候,會運行onStartup
* @param c @HandlesTypes註解中配置的接口的所有實現類
* @param ctx 可以用來註冊三大組件
* @throws ServletException
*/
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println("onStartup");
for (Class<?> aClass : c) {
System.out.println(aClass);
}
}
}
配置web三大組件
@HandlesTypes(value = {HelloServlet.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println("onStartup");
for (Class<?> aClass : c) {
System.out.println(aClass);
}
//形式註冊三大組件
//註冊組件 ServletRegistration
ServletRegistration.Dynamic servlet = ctx.addServlet("servletDemo4", new ServletDemo4());
//配置servlet的映射信息
servlet.addMapping("/demo4");
//註冊Listener
ctx.addListener(MyServletContextListener.class);
//註冊Filter FilterRegistration
FilterRegistration.Dynamic filter = ctx.addFilter("filterDemo1", FilterDemo1.class);
//配置Filter的映射信息
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
過濾器:
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter-init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Filter-doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("Filter-destroy");
}
}
監聽器:
public class MyServletContextListener implements ServletContextListener {
/**
* 監聽ServletContext對象創建
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("MyServletContextListener-contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
Servlet程序:
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo4");
}
}
Spring整合SpringMVC
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
從上面加載的spring-web的jar包中可以看到,也配置了javax.servlet.ServletContainerInitializer,內容是org.springframework.web.SpringServletContainerInitializer
,所以,Spring啓動的時候,會加載org.springframework.web.SpringServletContainerInitializer
類,並調用onStartup
方法,這個類的源碼如下:
// 會加載所有WebApplicationInitializer的實現類給onStartup方法的webAppInitializerClasses參數
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if (webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)waiClass.newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator();
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
initializer.onStartup(servletContext);
}
}
}
}
現在來看看WebApplicationInitializer
的實現類有哪些:
AbstractAnnotationConfigDispatcherServletInitializer
:註解方式配置的DispatcherServlet初始化器AbstractContextLoaderInitializer
:創建 根容器AbstractDispatcherServletInitializer
:
創建web ioc容器
創建DispatcherServlet
將創建的DispatcherServlet添加到ServletContext中
所以,如果我們希望以註解的方式來啓動SpringMVC,就繼承 AbstractAnnotationConfigDispatcherServletInitializer
類,實現抽象方法,具體如何配置請看官網(https://docs.spring.io/spring/docs/),這裏只是討論了註解配置的基本原理。