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);
}
}
}
@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");
}
}
<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
:所以,如果我们希望以注解的方式来启动SpringMVC,就继承 AbstractAnnotationConfigDispatcherServletInitializer
类,实现抽象方法,具体如何配置请看官网(https://docs.spring.io/spring/docs/),这里只是讨论了注解配置的基本原理。