Servlet2.5是JavaEE5.0规范,最低运行环境为JDK5.0以及Tomcat5.0。而Servlet3.0是JavaEE6.0规范,最低环境为JDK6.0以及Tomcat7.0。Servlet3.0的新特性主要分为以下几个点:
- 基于注解配置三大组件
- 文件上传API的优化
- 异步处理
- 组件可插性
- 动态注册三大组件(ServletContext)
- SPI - ServletContainerInitializer 机制
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
基于注解的配置
基于web.xml的配置与基于注解的配置,在Servlet3.0中,可以混合使用
@WebServlet
用来注册servlet组件,在servlet2.x中,需要在web.xml中配置servlet标签:
<servlet>
<!--servlet描述-->
<description>Servlet描述</description>
<!--servlet展示名称-->
<display-name>TestServlet</display-name>
<!--servlet名称-->
<servlet-name>TestServlet</servlet-name>
<!--servlet class-->
<servlet-class>com.yangsx95.demo.TestServlet</servlet-class>
<!--servlet 初始化参数-->
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>value2</param-value>
</init-param>
<!--servlet加载顺序-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--servlet url 映射-->
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
在3.0 可以使用如下方式注册一个Servlet:
//@WebServlet("test") // value 就是 urlPatterns,是默认属性,二者不能同时使用
@WebServlet(
description = "Servlet描述",
displayName = "TestServlet",
name = "TestServlet",
initParams = {@WebInitParam(name="param1", value = "value1"), @WebInitParam(name="param2", value = "value2")},
loadOnStartup = 1,
urlPatterns = {"test", "aaa"}
)
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取servlet名称
this.getServletName();
// 获取servlet初始化参数
Enumeration<String> initParameterNames = this.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
System.out.println(initParameterNames.nextElement());
}
resp.getWriter().append("servlet test");
}
}
@WebFilter
xml配置:
<filter>
<description>TestFilter描述</description>
<display-name>TestFilter</display-name>
<!--Filter名称-->
<filter-name>TestFilter</filter-name>
<!--Filter class-->
<filter-class>com.yangsx95.demo.TestFilter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>value2</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<!--filter 过滤规则-->
<!--过滤路径-->
<!--<url-pattern>/*</url-pattern>-->
<!--过滤指定的servlet-->
<servlet-name>TestServlet</servlet-name>
</filter-mapping>
对应的Servlet3.0 注解配置:
@WebFilter(
displayName = "TestFilter",
filterName = "TestFilter",
initParams = {@WebInitParam(name="param1", value = "value1"), @WebInitParam(name="param2", value = "value2")},
// urlPatterns = {"/*"}, // 过滤路径
servletNames = {"TestServlet"} // 过滤指定的servlet
)
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取初始化参数
// filterConfig.getInitParameter();
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("-------------start-----------");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("--------------end------------");
}
@Override
public void destroy() {
}
}
@WebListener
<listener>
<listener-class>com.yangsx95.demo.TestListener</listener-class>
</listener>
对应的Servlet3.0注解方式也很简单:
@WebListener
public class TestListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("应用程序启动");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("应用程序关闭");
}
}
文件上传API
@WebServlet("/upload")
@MultipartConfig // 代表该servlet可以处理multipart请求
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取服务器本地路径
String txtPath = this.getServletContext().getRealPath("/txts");
Part file = req.getPart("file");// 文件表单对应的input name
// 获取指定的头部属性
// file.getHeader("Content");
file.write(txtPath + File.separator + "111.txt"); // 将文件写入到磁盘
}
}
异步处理
部分servlet需要做一些很长的耗时操作,这时,就需要servlet提前响应前端用户,自己后台继续执行逻辑,比如邮件发送、短信通知、复杂耗时任务提前返回处理中。这就是servlet的异步处理。
下面是代码展示:
@WebServlet(value = "/register", asyncSupported = true) // 开启异步支持
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取异步上下文对象,包含request信息,response信息
AsyncContext ac = req.getAsyncContext();
// 创建异步Runnable对象
ComputeRunnable computeRunnable = new ComputeRunnable(ac);
// 异步处理超时时间
ac.setTimeout(30 * 1000);
// 设置异步监听器
ac.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("异步任务完成");
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("异步任务超时");
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("异步任务出错");
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("异步任务开始执行");
}
});
// 异步执行runnable对象
ac.start(computeRunnable);
// 立即响应
resp.getWriter().print("成功,稍后会给您发送邮件");
}
}
public class ComputeRunnable implements Runnable {
private AsyncContext asyncContext;
public ComputeRunnable(AsyncContext context) {
this.asyncContext = context;
}
@Override
public void run() {
System.out.println(asyncContext.getRequest());
System.out.println(asyncContext.getResponse());
}
}
组件可插性
JavaEE6.0项目支持将打为Jar包的Servlet、Filter、Listener直接插入到运行的web项目中,这些jar中包含相应的配置文件,这些jar也称为web片断项目。
定义一个web-fragment项目很容易,只需要在web-inf下创建文件web-fragment即可:
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
version="3.0">
<name>WebFragment1</name>
<servlet>
<servlet-name>hi</servlet-name>
<servlet-class>com.yangsx95.HiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hi</servlet-name>
<url-pattern>/hi.view</url-pattern>
</servlet-mapping>
</web-fragment>
将此项目打包为jar,然后放入web项目的lib目录下,就可以访问web片段项目中的servlet了。
动态注册组件
也就是在代码中,通过一些api,比如addServlet动态添加组件,处于安全考虑,组件的动态注册,只能在应用启动时执行。
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// 获取ServletContext
ServletContext servletContext = servletContextEvent.getServletContext();
ServletRegistration.Dynamic servletName = servletContext.addServlet("servletName", TestServlet.class);
servletName.addMapping("/test");
FilterRegistration.Dynamic filterName = servletContext.addFilter("filterName", TestFilter.class);
filterName.setInitParameter("param2", "value2");
filterName.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
servletContext.addListener( TestListener.class);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
ServletContainerInitializer
Serlvet容器在启动容器的时候,会扫描当前应用中的每个jar中的
META-INF/services/javax.servlet.ServletContainerInitializer
中指定的实现类,启动并运行这个实现类的方法。这是一种SPI机制。
示例代码:
@HandlesTypes({Person.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
// 参数1是 HandlesTypes 指定的类型集合。 将系统中所有HandlesType定义的类型的类,也就是子类/实现类(这里是Person类的子类/实现类),都传递给这个接口;
// 参数2是ServletContext对象,用于在启动时向应用程序动态注册组件
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
servletContext.addListener(MyServletContextListener.class);
}
}
配置
META-INF/services/javax.servlet.ServletContainerInitializer
:
com.yangsx95.demo.MyServletContainerInitializer