Jetty对ServletContainerInitializer的支持与Spring的应用
Posted 黄智霖-blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetty对ServletContainerInitializer的支持与Spring的应用相关的知识,希望对你有一定的参考价值。
目录
关于ServletContainerInitializer和@HandlesTypes
ServletContainerInitializer是一个定义在javax.servlet包中的接口,根据servlet规范,框架提供的 ServletContainerInitializer实现必须绑定在 jar包的META-INF/services目录中的一个叫做 javax.servlet.ServletContainerInitializer的文件,在其中指定ServletContainerInitializer的实现。容器启动时会根据规则寻找该文件,找到对应配置的ServletContainerInitializer实现类。并且当应用正在启动的时候,在任何的ServletListener的事件被触发之前,ServletContainerInitializer的onStartup方法将被调用:
public interface ServletContainerInitializer
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
注解HandlesTypes用于ServletContainerInitializer,在ServletContainerInitializer实现上的HandlesTypes 注解用于表示它感兴趣的一些类,HandlesTypes的value是一个Class数组,可以表示一个ServletContainerInitializer对多个类感兴趣。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlesTypes
Class<?>[] value();
这些感兴趣的类会以Set的结构当做入参传入其onStartup方法中。
Jetty支持
jetty仓库地址:https://github.com/eclipse/jetty.project
进入org.eclipse.jetty.annotations.AnnotationConfiguration类,找到其configure方法:
public void configure(WebAppContext context) throws Exception
......
//创建ServletContainerInitializer
createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
//会通过线程池并发扫描jar包收集class
scanForAnnotations(context);
......
//上面createServletContainerInitializerAnnotationHandlers会将获取的ServletContainerInitializer和
//对应HandlesTypes注解配置的类包装成ContainerInitializer,最终存入context中
//这里取出来遍历调用其resolveClasses方法完成解析
//这里还涉及到org.eclipse.jetty.classInheritanceMap配置,不是我们的关注点
List<ContainerInitializer> initializers =
(List<ContainerInitializer>)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
if (initializers != null && initializers.size()>0)
//在上面scanForAnnotations方法中完成数据填充
Map<String, Set<String>> map = ( Map<String, Set<String>>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
for (ContainerInitializer i : initializers)
//处理感兴趣的类,主要是根据Class获取对应的实现类名,保存下来
i.resolveClasses(context,map);
其中会调用getNonExcludedInitializers()方法获取ServletContainerInitializer列表,如果指定了顺序,还会对ServletContainerInitializer列表进行排序(代码经过了精简和改写,只保留了部分逻辑):
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context) throws Exception
ArrayList<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
//通过ServiceLoader加载并实例化ServletContainerInitializer
ServiceLoader<ServletContainerInitializer> loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class);
//获取排序配置(org.eclipse.jetty.containerInitializerOrder)
ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context);
//迭代
for (ServletContainerInitializer sci:loadedInitializers)
//存入 sciResourceMap
sciResourceMap.put(sci, sciResource);
//假设根据containerInitializerOrder排序
nonExcludedInitializers.addAll(sciResourceMap.keySet());
//
Collections.sort(nonExcludedInitializers, new ServletContainerInitializerComparator(initializerOrdering));
//返回ServletContainerInitializer实现集合
return nonExcludedInitializers;
在通过getNonExcludedInitializers方法获取到ServletContainerInitializer的集合之后,会将其当做入参传入createServletContainerInitializerAnnotationHandlers方法,完成HandlesTypes注解的解析和ServletContainerInitializer的包装工作:
public void createServletContainerInitializerAnnotationHandlers (WebAppContext context, List<ServletContainerInitializer> scis) throws Exception
//ServletContainerInitializer会被包装为ContainerInitializer
List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
//将initializers 存入context,后续在ServletContainerInitializersStarter取出来使用
context.setAttribute(CONTAINER_INITIALIZERS, initializers);
for (ServletContainerInitializer service : scis)
//获取HandlesTypes注解
HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class);
ContainerInitializer initializer = null;
if (annotation != null)
//获取HandlesTypes配置的感兴趣的类
Class<?>[] classes = annotation.value();
if (classes != null)
//包装成ContainerInitializer
initializer = new ContainerInitializer(service, classes);
if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
ConcurrentHashMap<String, ConcurrentHashSet<String>> map = new ClassInheritanceMap();
context.setAttribute(CLASS_INHERITANCE_MAP, map);
//后续会在AnnotationConfiguration.configure方法中解析
_classInheritanceHandler = new ClassInheritanceHandler(map);
else
initializer = new ContainerInitializer(service, null);
if (LOG.isDebugEnabled()) LOG.debug("No classes in HandlesTypes on initializer "+service.getClass());
else
initializer = new ContainerInitializer(service, null);
initializers.add(initializer);
ServletContainerInitializersStarter starter =(ServletContainerInitializersStarter)context.getAttribute(CONTAINER_INITIALIZER_STARTER);
if (starter != null)
throw new IllegalStateException("ServletContainerInitializersStarter already exists");
//创建ServletContainerInitializersStarter
starter = new ServletContainerInitializersStarter(context);
context.setAttribute(CONTAINER_INITIALIZER_STARTER, starter);
//添加ServletContainerInitializersStarter
context.addBean(starter, true);
可以看到ServletContainerInitializer被包装成了ContainerInitializer ,并且存入了context中,key为org.eclipse.jetty.containerInitializers。之后在AnnotationConfiguration.configure方法的最后会取出ContainerInitializer列表,然后遍历调用resolveClasses方法处理感兴趣的类,这里只需要关注会将这些类名存入Set中。
同时还创建了ServletContainerInitializersStarter交给context管理,这个starter继承了AbstractLifeCycle,具备生命周期,那么我们就直接到ServletContainerInitializersStarter的doStart方法中:
public void doStart()
//从context中根据org.eclipse.jetty.containerInitializers参数将ContainerInitializer列表取出来
List<ContainerInitializer> initializers = (List<ContainerInitializer>)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
if (initializers == null)
return;
for (ContainerInitializer i : initializers)
try
//遍历调用callStartup
i.callStartup(_context);
catch (Exception e)
LOG.warn(e);
throw new RuntimeException(e);
逻辑很简单,从context中把之前在AnnotationConfiguration中存入的ContainerInitializer列表取出来,然后遍历调用其callStartup方法。来看看ContainerInitializer.callStartup方法的实现:
public void callStartup(WebAppContext context)
throws Exception
//target就是ServletContainerInitializer
if (_target != null)
Set<Class<?>> classes = new HashSet<Class<?>>();
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
try
for (String s : _applicableTypeNames)
//_applicableTypeNames就是感兴趣的类名列表
//在前面调用resolveClasses方法根据_interestedTypes(HandlesTypes注解配置)得到
classes.add(Loader.loadClass(context.getClass(), s));
context.getServletContext().setExtendedListenerTypes(true);
if (LOG.isDebugEnabled())
long start = System.nanoTime();
//调用ServletContainerInitializer的onStartup方法,将感兴趣的类列表classes传入
_target.onStartup(classes, context.getServletContext());
else
_target.onStartup(classes, context.getServletContext());
finally
context.getServletContext().setExtendedListenerTypes(false);
Thread.currentThread().setContextClassLoader(oldLoader);
到这里就实现了前面描述的关于ServletContainerInitializer和@HandlesTypes的使用规范。
Spring对ServletContainerInitializer的应用
spring-web中有一个类SpringServletContainerInitializer,它实现了ServletContainerInitializer接口,并且按照规范,将其配置在jar包下面的META-INF/services/javax.servlet.ServletContainerInitializer文件中:
我们来看看SpringServletContainerInitializer类的实现:
//对WebApplicationInitializer接口感兴趣
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException
//webAppInitializerClasses就是WebApplicationInitializer接口的实现类
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
//实例化WebApplicationInitializer
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;
//根据Order注解对initializers进行排序
AnnotationAwareOrderComparator.sort(initializers);
servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
for (WebApplicationInitializer initializer : initializers)
//遍历执行onStartup方法
initializer.onStartup(servletContext);
编程式的servlet、filter等的集成都依赖WebApplicationInitializer实现,通过它可以实现一个web.xml的"替代"方案,而WebApplicationInitializer由依赖SpringServletContainerInitializer,SpringServletContainerInitializer又依赖servlet容器对ServletContainerInitializer相关规范的支持。
以上是关于Jetty对ServletContainerInitializer的支持与Spring的应用的主要内容,如果未能解决你的问题,请参考以下文章