在之前典型的Java Web程序中,部署描述符web.xml
是必不可少的,在这里我们需要配置各种组件,包括Servlet
、Filter
和Listener
等,如果使用过SpringMVC的话,应该会对在web.xml
中配置org.springframework.web.servlet.DispatcherServlet
十分熟悉。但是对于所有的配置文件都有的一个通病就是只有在程序部署的时候,一些配置项问题才能被发现,而不能在程序编译开发阶段就发现。所以如果通过编程的方法来设置配置项则是一个最好的选择,我们完全可以抛弃web.xml
这个部署描述符,而是换用从Servlet 3.0中提出的一个接口javax.servlet.ServletContainerInitializer
来完成Servlet容器的初始化,该接口的作用是当Web应用启动时,通知一些实现类来执行一些要求的Servlet
、Filter
和Listener
等的编程注册。
一、接口说明
ServletContainerInitializer
接口细节:
-
其中只有一个方法
onStartup(...)
,在执行任何ServletContext
监听器之前,由Servlet容器调用这个方法,该方法的主要作用是注册Web对象; -
实现
ServletContainerInitializer
的类必须使用@HandleTypes
注解进行标注,以便声明onStartup(...)
可以处理这个类的实例,所有该类、包括它的子类都会被传递给其中的Set<Class<?>> c
字段,如果没有这个注解,或者在该注解中使用的类不存在或没有相应的子类,则会传递给该参数一个null
值; -
这个接口被设计在
jar
文件中使用,而无法直接在war
包中直接使用,而是通过在jar
中完成定义,然后将jar
包作为Java Web的依赖包,该接口的实现类会通过Java的SPI
的注册查询机制被发现; -
如果要使用这个类,需要一个元数据文件来说明如何定位该接口的实现类,该文本文件在META-INF/services文件夹下,这个文本文件的名称为
javax.servlet.ServletContainerInitializer
,其中的内容是该接口的实现类的全限定名;
二、编程实现
1、构建jar
依赖包
首先搭建依赖包工程,这里使用Maven管理工程,依赖项只需要一个,如下:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
- 1
Maven项目目录如下:
-
其中
ServletContainerInitializer
的实现类为com.demo.CustomServletContainerInitializer
,为了在jar中加入META-INF/services
文件夹,只需要将该文件夹直接放到maven
的资源文件夹resources
下即可,在打包的时候就会在jar中实现该文件夹,并包括之前的文件; -
可以看出
META-INF/services
文件夹中的文本文件名为javax.servlet.ServletContainerInitializer
,其中的内容为com.demo.CustomServletContainerInitializer
,为该接口的实现类; -
在
CustomServletContainerInitializer
中的实现如下:@HandlesTypes({HttpServlet.class}) public class CustomServletContainerInitializer implements ServletContainerInitializer { public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException { for(Class<?> clazz: c){ System.out.println(clazz.getName()); } } }
- 1
该实现类在Java Web应用启动时,会调用该方法,传递
HttpServlet
类以及其子类。
2、构建Java Web程序
Java Web程序的工程目录如下:
-
在工程中必须有
webapp
这个目录,如果没有则会导致异常java.lang.IllegalArgumentException: Document base ... src\main\webapp does not exist or is not a readable directory
; -
这里我们定义一个
Servlet
类TestServlet
,通过使用注解@javax.servlet.annotation.WebServlet
来标识这个Servlet
,代码如下:@WebServlet(urlPatterns = "/testServlet", name = "testServlet") public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Writer writer = resp.getWriter(); writer.write("do get"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Writer writer = resp.getWriter(); writer.write("do post"); } }
- 1
这个注解是在应用启动的时候被容器处理,并且都对应的servlet也可以通过相应的URL来访问;
在程序启动的过程中,会发现传递进来很多的servlet子类和Filter的实现类,如下:
其中也包括我们自定义的一个TestServlet
。