Servlet3.0 新特性
Servlet3.0 的注解
Servlet 允许开发人员采用注解的方式来配置 Servlet、Filter、Listener。
Servlet3.0 规范在 javax.servlet.annotation 包下提供了如下注解。
@WebServlet:用于修饰一个 Servlet 类,用于部署 Servlet 类。
@WebInitParam:用于与 @WebServlet 或 @WebFilter 一起使用,为 Servlet、Filter 配置参数。
@WebListener:用于修饰 Listener 类,用于部署 Listener 类
@WebFilter:用于修饰 Filter 类,用于部署 Filter 类
@MultipartConfig:用于修饰 Servlet,指定该 Servlet 将会负责处理 multipart/form-data 类型的请求(主要用于文件上传)
@ServletSecurity:这是一个与 JAAS 有关的注解,修饰 Servlet 指定该 Servlet 的安全与授权控制
@HttpConstraint:用于与 @ServletSecurity 一起使用,用于指定该 Servlet 的安全与授权控制
@HttpMethodConstraint:用于与 @ServletSecurity 一起使用,用于指定该 Servlet 的安全与授权控制
Servlet3.0 提供的异步处理
在以前的 Servlet 规范中,如果 Servlet 作为控制器调用了一个耗时的业务方法,那么 Servlet 必须等到业务完全返回之后才会生成响应,这将使得 Servlet 对业务方法的调用变成一种阻塞式的调用,因此效率比较低。
Servlet3.0 规范引入了异步处理来解决这个问题,异步处理允许 Servlet 重新发起一条新线程去调用耗时的业务方法,这样就可避免等待。
Servlet3.0 的异步处理是通过 AsyncContext 类来处理的,Servlet 可通过 ServletRequest 的如下两个方法开启异步调用、创建 AsyncContext 对象。
AsyncContext start()
AsyncContext startAsync(ServletRequest, ServletResponse)
重复调用上面的方法将得到同一个 AsyncContext 对象。AsyncContext 对象代表异步处理的上下文,它提供了一些工具方法,可完成设置异步调用的超时时长,dispatch 用于请求、启动后台线程、获取 request、response 对象等功能。
AsyncServlet.java
package com.baiguiren; import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date; import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.annotation.*; @WebServlet(urlPatterns="/async", asyncSupported=true)
public class AsyncServlet extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<title>异步调用实例</title>");
out.println("进入 Servlet 的时间:" + new Date() + ".<br/>"); // 创建 AsyncContext,开始异步调用
AsyncContext actx = request.startAsync();
// 设置异步调用的超时时长
actx.setTimeout(60 * 1000);
// 启动异步调用的线程
actx.start(new GetBookTask(actx)); out.println("结束 Servlet 的时间:" + new Date() + ".<br/>");
out.flush();
}
}
GetBookTask.java
package com.baiguiren; import java.util.ArrayList;
import java.util.List; import javax.servlet.*; public class GetBookTask implements Runnable
{
private AsyncContext actx = null; public GetBookTask(AsyncContext actx)
{
this.actx = actx;
} public void run()
{
try {
// 等待 5 秒,以模拟业务的方法执行
Thread.sleep(5 * 1000);
ServletRequest request = actx.getRequest();
List<String> books = new ArrayList<String>();
books.add("book 1");
books.add("book 2");
books.add("book 3");
request.setAttribute("books", books);
actx.dispatch("/async.jsp");
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的 task 方法暂停 5 秒来模拟调用耗时的业务方法,最后调用 AsyncContext 的 dispatch 方法把请求 dispatch 到指定的 JSP 页面。
async.jsp
<%@ page contentType="text/html; charset=UTF-8" session="false" %>
<%@ taglib prefix="c" tagdir="/WEB-INF/tags" %>
<%@ page import="java.util.*" %> <c:forEach items="${books}" /> <%
out.println("业务调用结束的时间:" + new java.util.Date());
if (request.isAsyncStarted()) {
// 完成异步调用
request.getAsyncContext().complete();
}
%>
forEach.tag
<%@ tag pageEncoding="UTF-8" import="java.util.*" %> <%@ attribute name="items" required="true" type="java.util.List" %> <ul>
<%
for (Object s : items) {
%>
<li><%=s%></li>
<%
}
%>
</ul>
对于希望启用异步调用的 Servlet 而言,开发者必须显示指定开启异步调用,为 Servlet 开启异步调用有两种方式。
为 @WebServlet 指定 asyncSupported=true
在 web.xml 文件的 <servlet ../> 元素中增加 <async-supported .../> 子元素,该子元素的值为 true
对于支持异步调用的 Servlet 来说,当 Servlet 以异步方式启用新线程之后,该 Servlet 的执行并不会阻塞,该 Servlet 将可以向客户端浏览器生成响应 --- 当新线程执行完成后,新线程生成的响应再次被送往客户端浏览器。
当 Servlet 启用异步调用的线程之后,该线程的执行过程对开发者是透明的。但在有些情况下,开发者需要了解该异步线程的执行细节,并针对特定的执行结果进行针对性处理,这可借助于 Servlet3.0 提供的异步监听器来实现。
异步监听器需要实现 AsyncListener 接口,实现该接口的监听器类需要实现如下 4 个方法。
onStartAsync(AsyncEvent event):当异步调用开始时触发该方法
onComplete(AsyncEvent event):当异步调用完成时触发该方法
onError(AsyncEvent event):当异步调用出错时触发该方法
onTimeout(AsyncEvent event):当异步调用超时时触发该方法
MyAsyncListener.java
package com.baiguiren; import java.io.IOException;
import java.util.Date; import javax.servlet.*;
import javax.servlet.annotation.*; @WebListener
public class MyAsyncListener implements AsyncListener
{
public void onComplete(AsyncEvent event) throws IOException
{
System.out.println("---异步调用完成---" + new Date());
} public void onError(AsyncEvent event) throws IOException
{
} public void onStartAsync(AsyncEvent event) throws IOException
{
System.out.println("---异步调用开始---" + new Date());
} public void onTimeout(AsyncEvent event) throws IOException
{
}
}
虽然上面的 Listener 监听器类可以监听异步调用开始、异步调用完成两个时间,但从实际运行的结果来看,它并不能监听到异步调用开始事件,这可能是因为注册该监听器时异步调用已经开始了的缘故。
改进的 Servlet API
Servlet3.0 还有一个改变是改进了部分 API,这种改进很好地简化了 java web 开发。其中两个较大的改进是:
HttpServletRequest 增加了对文件上传的支持
ServletContext 允许通过编程的方式动态注册 Servlet、Filter。
HttpServletRequest 提供了如下两个方法来处理文件上传:
Part getPart(String name):根据名称来获取文件上传域
Collection<Part> getParts():获取所有的文件上传域
上面两个方法的返回值都涉及一个 API:Part,每个 Part 对象对应于一个文件上传域,该对象提供了大量方法来访问上传文件的文件类型、大小、输入流等,并提供了一个 write(String file) 方法将上传文件写入磁盘。
upload.jsp
<%@ page contentType="text/html; charset=UTF-8" %> <html>
<head>
<title>文件上传</title>
</head>
<body>
<form method="post" action="upload" enctype="multipart/form-data">
文件名:<input type="text" id="name" name="name" /><br/>
选择文件:<input type="file" id="file" name="file"/><br/>
<input type="submit" value="上传" /><br/>
</form>
</body>
</html>
UploadServlet.java
package com.baiguiren; import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection; import javax.servlet.*;
import javax.servlet.jsp.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*; @WebServlet(name="upload", urlPatterns={"/upload"})
@MultipartConfig
public class UploadServlet extends HttpServlet
{
public void service(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
request.setCharacterEncoding("UTF-8");
// 获取普通请求参数
String name = request.getParameter("name");
out.println("普通的 name 参数为:" + name + "<br/>"); // 获取文件上传域
Part part = request.getPart("file");
// 获取上传文件的文件类型
out.println("上传文件的类型为:" + part.getContentType() + "<br/>");
// 获取上传文件的大小
out.println("上传文件的大小为:" + part.getSize() + "<br/>");
// 获取该文件上传域的 Header Name
Collection<String> headerNames = part.getHeaderNames();
// 遍历文件上传域的 Header Name、Value
for (String headerName : headerNames)
{
out.println(headerName + "---->" + part.getHeader(headerName) + "<br/>");
}
// 获取包含文件原始名的字符串
String fileNameInfo = part.getHeader("content-disposition");
// 提取上传文件的原始文件名
String fileName = fileNameInfo.substring(fileNameInfo.indexOf("filename=\"") + 10, fileNameInfo.length() - 1);
// 将上传的文件写入服务器
part.write(getServletContext().getRealPath("/uploadFiles") + "/" + fileName);
}
}
上面 Servlet 使用了 @MultipartConfig 修饰,处理文件上传的 Servlet 应该使用该注解修饰。也可以在 web.xml 中的 <servlet.../> 子元素中添加 <multipart-config.../> 子元素来达到同样效果。
ServletContext 则提供了如下方法来动态地注册 Servlet、Filter,并允许动态设置 Web 应用的初始化参数。
多个重载的 addServlet() 方法:动态地注册 Servlet
多个重载的 addFilter() 方法:动态地注册 Filter
多个重载的 addListener() 方法:动态地注册 Listener
setInitParameter(String name, String value) 方法:为 web 应用设置初始化参数。
Servlet3.1 新增的非阻塞式 IO
Servlet 底层的 IO 是通过如下两个 IO 流来支持的。
ServletInputStream:Servlet 用于读取数据的输入流。
ServletOutputStream:Servlet 用于输出数据的输出流。
以 Servlet 读取数据为例,传统的读取方式采用阻塞式 IO -- 当 Servlet 读取浏览器提交的数据时,如果数据暂时不可用,或数据没有读取完成,Servlet 当前所在线程将会被阻塞,无法继续向下执行。
从 Servlet 3.1 开始,ServletInputStream 新增了一个 setReadListener(ReadListener readListener) 方法,该方法允许以非阻塞式 IO 读取数据,实现 ReadListener监听器需要实现如下三个方法。
onAllDataRead():当所有数据读取完成时触发该方法
onDataAvailable():当有数据可用时触发该方法
onError(Throwable t):当读取数据出现错误时触发该方法
类似地,ServletOutputStream 也提供了 setWriterListener(WriteListener writeListener) 方法,通过这种方式,可以让 ServletOutputStream 以非阻塞 IO 进行输出。
在 Servlet 中使用非阻塞 IO 非常简单,按如下步骤进行即可。
1、调用 ServletRequest 的 startAsync() 方法开启异步模式。
2、通过 ServletRequest 获取 ServletInputStream,并为 ServletInputStream 设置监听器(ReadListener 实现类)。
3、实现 ReadListener 接口实现监听器,在该监听器的方法中以非阻塞方式获取数据。
AsyncServlet1.java
package com.baiguiren; import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date; import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.annotation.*; @WebServlet(urlPatterns="/async1", asyncSupported=true, loadOnStartup=1)
public class AsyncServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<title>非阻塞 IO 实例</title>");
out.println("进入 Servlet 的时间:" + new Date() + ".<br/>"); // 创建 AsyncContext,开始异步调用
AsyncContext actx = request.startAsync();
// 设置异步调用的超时时长
actx.setTimeout(60 * 1000); // 为输入流注册监听器
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, actx)); out.println("结束 Servlet 的时间:" + new Date() + ".<br/>");
out.flush();
}
}
MyReadListener.java
package com.baiguiren; import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.annotation.*; public class MyReadListener implements ReadListener
{
private ServletInputStream input; private AsyncContext context; public MyReadListener(ServletInputStream input, AsyncContext context)
{
this.input = input;
this.context = context;
} @Override
public void onDataAvailable()
{
System.out.println("数据可用!"); try {
// 暂停 5 秒,模拟耗时操作
Thread.sleep(5000);
StringBuilder sb = new StringBuilder();
int len = -1;
byte[] buff = new byte[1024]; // 采用原始 IO 方式读取浏览器向 Servlet 提交的数据
while (input.isReady() && (len = input.read(buff)) > 0)
{
String data = new String(buff, 0, len);
sb.append(data);
} System.out.println(sb);
// 将数据设置为 request 范围的属性
context.getRequest().setAttribute("info", sb.toString()); // 转发到视图页面
context.dispatch("/async1.jsp");
} catch (Exception ex) {
ex.printStackTrace();
}
} @Override
public void onAllDataRead()
{
System.out.println("数据读取完成");
} @Override
public void onError(Throwable t)
{
t.printStackTrace();
}
}
async1.jsp
<%@ page contentType="text/html; charset=UTF-8" session="false" %> <%
out.println("业务调用结束的时间:" + new Date()); out.println(request.getAttribute("info"));
if (request.isAsyncStarted()) {
// 完成异步调用
request.getAsyncContext().complete();
}
%>
上面程序中的 MyReadListener 的 onDataAvailable() 方法先暂停 5 秒,用于模拟耗时操作,接下来程序使用普通 IO 流读取浏览器提交的数据。
如果程序直接让 Servlet 读取浏览器提交的数据,那么该 Servlet 就需要阻塞 5 秒,不能继续向下执行;改为非阻塞 IO 进行读取,虽然读取数据的 IO 操作需要 5 秒,但它不会阻塞 Servlet 执行,因此可以提升 Servlet 的性能。
Servlet3.0 新特性的更多相关文章
- Servlet3.0新特性
1 Servlet3.0新特性概述 使用要求:MyEclipse10.0或以上版本,发布到Tomcat7.0或以上版本,创建JavaEE6.0应用! Servlete3.0的主要新特性如下三部分: 使 ...
- 【servlet3.0新特性】Annotation注解配置
servlet3.0新特性Servlet3.0引入的若干重要新特性,包括异步处理.新增的注解支持.可插性支持等等,为读者顺利向新版本过渡扫清障碍.Servlet3.0新特性概述Servlet3.0作为 ...
- 【Servlet3.0新特性】第03节_文件上传
这是一个Web Project 首先是web.xml <?xml version="1.0" encoding="UTF-8"?> <web- ...
- 使用Servlet3.0新特性asyncSupported=true时抛异常java.lang.IllegalStateException: Not supported
最近在运用Servlet3.0新特性:异步处理功能的时候出现以下了2个问题: 运行时会抛出以下两种异常: 一月 19, 2014 3:07:07 下午 org.apache.catalina.core ...
- Java基础加强-(注解,动态代理,类加载器,servlet3.0新特性)
1. Annotation注解 1.1. Annotation概述 Annotation是JDK 5.0以后提供对元数据的支持,可以在编译.加载和运行时被读取,并执行相应的处理.所谓Annota ...
- Servlet3.0新特性(从注解配置到websocket编程)
Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了web开发的效率.主要新特性有以下几个: 引入注解配置 支持 ...
- Java自学手记——servlet3.0新特性
servlet3.0出来已经很久了,但市场上尚未普遍应用,servlet3.0有三个比较重要的新特性:使用注解来代替配置文件,异步处理以及上传组件支持. 支持servlet3.0的要求:MyEclip ...
- Servlet3.0新特性WebFilter(Annotation Filter)详解
摘要: Servlet3.0作为J2EE 6规范一部分,并随J2EE6一起发布,WeFilter是过滤器注解,是Servlet3.0的新特性,不需要在web.xml进行配置,简化了配置. Name T ...
- Servlet3.0新特性使用详解
可插拔的Web框架 几乎所有基于Java的web框架都建立在servlet之上.现今大多数web框架要么通过servlet.要么通过Web.xml插入.利用标注(Annotation)来定义servl ...
- servlet3.0 新特性——异步处理
Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下: 首先,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理: 接着,调用业务接口的某些方法,以完成业 ...
随机推荐
- 【BUG】12小时制和24小时制获取当天零点问题
[BUG]12小时制和24小时制获取当天零点问题 最近在写定时服务的时候,要获取当天的零点这个时间,但是是这样获取的 DateTime dt = DateTime.Parse(DateTime.Now ...
- 首次使用windows管理界面访问安装在UNIX或linux下的DP服务器时提示无权限访问的解决方法
用windwos GUI管理界面连接时提示无权限访问: 在/etc/opt/omni/server/users/userlist 添加一行: "" "*" &q ...
- 亚马逊6月18日发布惊世之作 或为3D智能手机
亚马逊将在 6 月 18 日举行一个产品发布会. 其内容可能是关于传闻已久的亚马逊智能手机.该公司在 YouTube 上公布了一段炫耀这款设备的视频.这段视频展示了很多人在这款产品前摇头晃脑,并且表现 ...
- 获取文件夹下某个类型的文件名---基于python
方法1:import osclass flist_name(): def __init__(self,path): self.flist_name=os.listdir(path) def pcap_ ...
- Android 对话框(Dialogs)
对话框是提示用户作出决定或输入额外信息的小窗口. 对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件. 1.对话框设计 如需了解有关如何设计对话框的信息(包括语言建议),请阅读对话框设 ...
- 武汉天喻信息 移动安全领域 SE(Secure Element)
产品简介: SE(Secure Element)为安全模块,是一台微型计算机,通过安全芯片和芯片操作系统(COS)实现数据安全存储.加解密运算等功能.SE可封装成各种形式,常见的有智能卡和嵌入式安全模 ...
- DoItYourself!
在杨老师的勉励下,我准备开始“自己”写程序.速度很慢,不过在写的过程中对于用到的几个函数更加熟悉.也尝试多学一点,学透一点.遇到不会的函数,语法不清楚的,还是会百度,不过会自己再敲一遍.重复下去. 下 ...
- “Hello World!团队”Beta发布—视频链接+文案+美工
视频链接:http://v.youku.com/v_show/id_XMzE3MjEyMzkyMA==.html?spm=a2h3j.8428770.3416059.1 文案+美工:http://ww ...
- 测试与优化bugbugbugbug
单元测试
- linux awk,sort,uniq,wc,cut命令详解
1.awk awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息 $ 表示当前行 $ 表示第一列 NF 表示一共有多少列 $NF 表示最 ...