ServletContainerInitializer及WebApplicationInitializer

ServletContainerInitializer即Servlet容器初始化组件,用来初始化Servlet容器。如注册Servlet、注册过滤器等。

public interface ServletContainerInitializer {

    /**
     * Receives notification during startup of a web application of the classes
     * within the web application that matched the criteria defined via the
     * {@link javax.servlet.annotation.HandlesTypes} annotation.
     *
     * @param c     The (possibly null) set of classes that met the specified
     *              criteria
     * @param ctx   The ServletContext of the web application in which the
     *              classes were discovered
     *
     * @throws ServletException If an error occurs
     */
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

为了解耦,JAVA提供了一种约定,服务提供方实现了ServletContainerInitializer后,在jar包的META-INF/services目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容为对应的实现类。
如Spring实现的SpringServletContainerInitializer,见下图
在这里插入图片描述
同时对于ServletContainerInitializer组件可以添加注解@HandlesTypes,可以指定具体的类名,被指定的类的实现将被扫描并注入到ServletContainerInitializer.onStartup方法的参数Set<Class<?>>中。

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer) waiClass.newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

如上的SpringServletContainerInitializer,会将所有的WebApplicationInitializer实现注入其参数Set<Class<?>> webAppInitializerClasses中,并进行实例化及遍历调用,用于初始化Web环境上下文。

WebApplicationInitializer的实现替代了web.xml的配置方式,如我们在使用传统SpringMVC时需要配置在web.xml中配置DispatcherServlet,如

 * <servlet>
 *   <servlet-name>dispatcher</servlet-name>
 *   <servlet-class>
 *     org.springframework.web.servlet.DispatcherServlet
 *   </servlet-class>
 *   <init-param>
 *     <param-name>contextConfigLocation</param-name>
 *     <param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
 *   </init-param>
 *   <load-on-startup>1</load-on-startup>
 * </servlet>
 *
 * <servlet-mapping>
 *   <servlet-name>dispatcher</servlet-name>
 *   <url-pattern>/</url-pattern>
 * </servlet-mapping>

可以改为

public class MyWebAppInitializer implements WebApplicationInitializer {
     @Override
     public void onStartup(ServletContext container) {
       XmlWebApplicationContext appContext = new XmlWebApplicationContext();
       appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
       ServletRegistration.Dynamic dispatcher =
         container.addServlet("dispatcher", new DispatcherServlet(appContext));
       dispatcher.setLoadOnStartup(1);
       dispatcher.addMapping("/");
     }
 }

SpringBoot提供了一个抽象类SpringBootServletInitializer implements WebApplicationInitializer提供默认实现,如果要自定义可以继承它,并在configure中指定应用启动类。注意只有打包为war时才会用到它,嵌入式启动不需要。

public class Bootstrap extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
}

Tomcat通过ContextContextConfig来实现ServletContainerInitializer组件的调用和加载。
ContextConfig会遍历所有jar包的javax.servlet.ServletContainerInitializer的文件内容,通过反射实例化对象,并将其注入到Context中,并由Context调用其onStartup方法。
在SpringBoot的嵌入式Tomcat中,实现了class TomcatStarter implements ServletContainerInitializer,并将其硬编码直接注入到了WebApplicationContext中,TomcatStarter中会遍历调用所有ServletContextInitializer来实现Servlet容器上下文的初始化

其他参考资料:

  1. https://blog.youkuaiyun.com/cmmchenmm/article/details/83115645
  2. https://blog.youkuaiyun.com/j080624/article/details/80016905
  3. https://www.cnblogs.com/sword-successful/p/11383723.html
  4. https://blog.youkuaiyun.com/classicer/article/details/50753019
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值