ServletContainerInitializer

Servlet 3.0+ ServletContainerInitializer旨在支持Servlet容器的基于代码的配置。
Servlet 3.0+ 规范在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册Servlet或者Filtes等。哪它是如何实现的呢?

实现原理:

  • 首先了解下Java SPI(Service Provider Interface),它是Java提供的一套用来被第三方实现或者扩展的API需要遵循如下约定:
    • 当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
    • 接口实现类所在的jar包放在主程序的classpath中;
    • 主程序通过java.util.ServiceLoder.load(Class)动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
    • SPI的实现类必须携带一个不带参数的构造方法;
  • 任何Servlet 3.0兼容的容器通过Java SPI机制查找到 javax.servlet.ServletContainerInitializer (这是servlet3.0规范中新增的一个接口)的实现类。
  • @HandlesTypes注解用于注释ServletContainerInitializer的实现类,然后容器会将当前应用中所有这一类型(继承或者实现)的类放在ServletContainerInitializer接口的集合参数c中传递进来。如果不定义处理类型,或者应用中不存在相应的实现类,则集合参数c为空。
  • 容器启动时分别调用所有ServletContainerInitializer实现的onStartup方法,并将对应实现类 @HandlesTypes注解指定类型(继承或者实现)的类和ServletContext当作参数传入,最后实现类拿到ServletContext就可以进行操作了,比如:注册Servlet或者Filtes

SpringMVC中的应用

  • org.springframework.web.SpringServletContainerInitializer正是ServletContainerInitializer的实现类。
@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) {
				// 这里关注WebApplicationInitializer的子类
				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);
		}
	}
}
  • 在spring-web-4.3.25.RELEASE.jar中也可以看到ServletContainerInitializer实现的声明
    在这里插入图片描述
  • Spring的WebApplicationInitializer SPI只包含一个方法:WebApplicationInitializer.onstartup (ServletContext)。这里又通过WebApplicationInitializer接口来让用户拿到ServletContext对象。
public interface WebApplicationInitializer {
	void onStartup(ServletContext servletContext) throws ServletException;
}
  • 这样SpringServletContainerInitializer负责将ServletContext实例化并委托给任何用户定义的WebApplicationInitializer实现。
    • WebApplicationInitializer多个实例还可以通过@Order注解和实现Ordered接口来来确保servlet容器初始化的顺序。
    • WEB-INF/web.xml和WebApplicationInitializer的使用并不互斥;例如,web.xml可以注册一个servlet,而WebApplicationInitializer可以注册另一个servlet;初始化器甚至可以通过。ServletContext.getServletRegistration(String)等方法修改在web.xml中执行的注册。
    • 如果应用程序中存在WEB-INF/web.xml,则必须将其版本属性设置为“3.0”或更高,否则servlet容器将忽略ServletContainerInitializer的引导。
    • 在Tomcat 7.0.14及以下版本,这个servlet映射不能以编程方式覆盖,7.0.15修复了这个问题。

参考Servlet3.0研究之ServletContainerInitializer接口

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值