概述
ServletContainerInitializer是Servlet3.0规范中引入,目的是通过编程的手段创建web应用,而不是传统的web.xml方式。spring中实现了该接口。
ServletContainerInitializer通过JAR包中的
META-INF/services/javax.servlet.ServletContainerInitializer
文件中的一个条目注册,JAR包中必须包含了ServletContainerInitializer接口的实现。
ServletContainerInitializer通过javax.servlet.annotaion.HandelsTypes注解注册需要感知的类。
当web应用程序中的类在启动时,ServletContainerInitializer的onStartup方法将收到那些满足javax.servlet.annotaion.handlesTypes类的通知。
示例
spring使用了ServletContainerInitializer进行应用初始化。
org.springframework.web.SpringServletContainerInitializer
@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>();
// 省略......
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
SpringServletContainerInitializer监听的是WebApplicationInitializer类,servlet容器启动时将会调用WebApplicationInitializer的onStartup方法完成应用程序的初始化。
Tomcat容器初始化
tomcat在启动过程中也非常简单 ,它会扫描所有实现了ServletContainerInitializer的接口,通过获取HandlesTypes的注解,将ServletContainerInitializer与对应的类(由HandlesTypes注解指定)绑定在typeInitializerMap中。
protected void processServletContainerInitializers() {
List<ServletContainerInitializer> detectedScis;
try {
WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
detectedScis = loader.load(ServletContainerInitializer.class);
} catch (IOException e) {
log.error(sm.getString(
"contextConfig.servletContainerInitializerFail",
context.getName()),
e);
ok = false;
return;
}
for (ServletContainerInitializer sci : detectedScis) {
initializerClassMap.put(sci, new HashSet<>());
HandlesTypes ht;
try {
ht = sci.getClass().getAnnotation(HandlesTypes.class);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.info(sm.getString("contextConfig.sci.debug",
sci.getClass().getName()),
e);
} else {
log.info(sm.getString("contextConfig.sci.info",
sci.getClass().getName()));
}
continue;
}
if (ht == null) {
continue;
}
Class<?>[] types = ht.value();
if (types == null) {
continue;
}
for (Class<?> type : types) {
if (type.isAnnotation()) {
handlesTypesAnnotations = true;
} else {
handlesTypesNonAnnotations = true;
}
Set<ServletContainerInitializer> scis =
typeInitializerMap.get(type);
if (scis == null) {
scis = new HashSet<>();
typeInitializerMap.put(type, scis);
}
scis.add(sci);
}
}
}
在Tomcat的ServletContext类中,通过startInternal方法完成ServletContainerInitializer的调用(调用ServletContainerInitializer的onStartup方法)。
entry.getKey().onStartup(entry.getValue(),getServletContext())
// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}