在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过<code>ServletContainerInitializer</code>实现此功能。每个框架要使用<code>ServletContainerInitializer</code>就必须在对应的jar包的META-INF/services 目录创建一个名为<code>javax.servlet.ServletContainerInitializer</code>的文件,文件内容指定具体的<code>ServletContainerInitializer</code>实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
一般伴随着<code>ServletContainerInitializer</code>一起使用的还有<code>HandlesTypes</code>注解,通过<code>HandlesTypes</code>可以将感兴趣的一些类注入到<code>ServletContainerInitializerde</code>的onStartup方法作为参数传入。
Tomcat容器的<code>ServletContainerInitializer</code>机制的实现,主要交由Context容器和ContextConfig监听器共同实现,ContextConfig监听器负责在容器启动时读取每个web应用的<code>WEB-INF/lib</code>目录下包含的jar包的<code>META-INF/services/javax.servlet.ServletContainerInitializer</code>,以及web根目录下的<code>META-INF/services/javax.servlet.ServletContainerInitializer</code>,通过反射完成这些<code>ServletContainerInitializer</code>的实例化,然后再设置到Context容器中,最后Context容器启动时就会分别调用每个<code>ServletContainerInitializer</code>的onStartup方法,并将感兴趣的类作为参数传入。
基本的实现机制如图,首先通过ContextConfig监听器遍历每个jar包或web根目录的<code>META-INF/services/javax.servlet.ServletContainerInitializer</code>文件,根据读到的类路径实例化每个<code>ServletContainerInitializer</code>;然后再分别将实例化好的<code>ServletContainerInitializer</code>设置进Context容器中;最后Context容器启动时分别调用所有<code>ServletContainerInitializer</code>对象的onStartup方法。
假如读出来的内容为<code>com.seaboat.mytomcat.CustomServletContainerInitializer</code>,则通过反射实例化一个<code>CustomServletContainerInitializer</code>对象,这里涉及到一个<code>@HandlesTypes</code>注解的处理,被它标明的类需要作为参数值传入到onStartup方法。如下例子:
其中<code>@HandlesTypes</code>标明的HttpServlet和Filter两个class被注入到了onStartup方法。所以这个注解也是需要在ContextConfig监听器中处理。前面已经介绍了注解的实现原理,由于有了编译器的协助,我们可以方便地通过<code>ServletContainerInitializer</code>的class对象中获取到HandlesTypes对象,进而再获取到注解声明的类数组,如
即可获取到HttpServlet和Filter的class对象数组,后面Context容器调用<code>CustomServletContainerInitializer</code>对象的onStartup方法时作为参数传入。至此,即完成了servlet规范的<code>ServletContainerInitializer</code>初始化器机制。
点击订购作者《Tomcat内核设计剖析》