记得servlet3的特性有以下几点:
1、异步处理支持:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将耗时的操作委派给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,这将大大减少服务器资源的占用,并且提高并发处理速度。
2、新增的注解支持:该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。
3、可插性支持:熟悉 Struts2 的开发者一定会对其通过插件的方式与包括 Spring 在内的各种常用框架的整合特性记忆犹新。将相应的插件封装成 JAR 包并放在类路径下,Struts2 运行时便能自动加载这些插件。现在 Servlet 3.0 提供了类似的特性,开发者可以通过插件的方式很方便的扩充已有 Web 应用的功能,而不需要修改原有的应用。
以前的servlet的流程:
首先,Servlet接收到请求之后,可能需要对请求携带的数据进行一些处理;接着,调用业务接口的某些方法,已完成业务处理;最后根据处理的结果提交响应,Servlet线程结束。其中第二步的业务处理通常是最耗时的,这主要体现数据库操作,以及其它的跨网络调用等、在此过程中、Servlet线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务过程中、Servlet资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能瓶颈,对此,在以前通常采用私有解决方案,来提前结束Servlet线程,并及时释放资源。
Servlet3.0流程:
首先,Servlet接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接着,Servlet线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器、此时Servlet还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有ServletRequest和ServletResponse对象的引用),或者将请求继续转发给其它的Servlet,如此一来,Servlet线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。
注意:
Servlet 3.0 还为异步处理提供了一个监听器,使用 AsyncListener 接口表示。它可以监控如下四种事件:
异步线程开始时,调用 AsyncListener 的 onStartAsync(AsyncEvent event) 方法;
异步线程出错时,调用 AsyncListener 的 onError(AsyncEvent event) 方法;
异步线程执行超时,则调用 AsyncListener 的 onTimeout(AsyncEvent event) 方法;
异步执行完毕时,调用 AsyncListener 的 onComplete(AsyncEvent event) 方法;
要注册一个 AsyncListener,只需将准备好的 AsyncListener 对象传递给 AsyncContext 对象的 addListener() 方法即可。
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value = {"/http23"}, asyncSupported = true)
public class Http2Servlet3 extends HttpServlet {
private Queue messages = new ConcurrentLinkedQueue();
private final Executor executor = Executors.newFixedThreadPool( 10 );
private List ctxs = new ArrayList();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType( "text/plain" );
res.setCharacterEncoding( "utf-8" );
res.setHeader( "Access-Control-Allow-Origin", "*" );
PrintWriter writer = res.getWriter();
writer.print( "2;Hi;" );
writer.flush();
final AsyncContext ctx = req.startAsync();
ctx.addListener( new AsyncListener() {
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
ctxs.remove( ctx );
}
@Override
public void onError(AsyncEvent event) throws IOException {
ctxs.remove( ctx );
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
ctxs.remove( ctx );
}
} );
ctxs.add( ctx );
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType( "text/plain" );
res.setCharacterEncoding( "utf-8" );
messages.add( createRandomMessage() );
}
@Override
public void init() throws ServletException {
super.init();
// produce random messages
//生成随机消息
new Thread( new Runnable() {
@Override
public void run() {
while (true) {
messages.add( createRandomMessage() );
try {
Thread.sleep( new Random().nextInt( 5 ) * 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} ).start();
// print messages to all users
//向所有用户打印message
new Thread( new Runnable() {
@Override
public void run() {
while (true) {
if (!messages.isEmpty()) {
final String message = messages.poll();
executor.execute( new Runnable() {
@Override
public void run() {
for (AsyncContext ctx : ctxs) {
try {
PrintWriter writer = ctx.getResponse().getWriter();
writer.print( message.length() );
writer.print( ';' );
writer.print( message );
writer.print( ';' );
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
};
} );
}
}
}
} ).start();
}
protected String createRandomMessage() {
return DateFormat.getTimeInstance().format( Calendar.getInstance().getTime() ) + ' ' + UUID.randomUUID().toString();
}
}
AsyncDemoServlet.java
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
@WebServlet(urlPatterns = "/servlet3", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
resp.setContentType( "text/html;charset=UTF-8" );
PrintWriter out = resp.getWriter();
out.println( "进入Servlet的时间:" + new Date() + "." );
out.flush();
//在子线程中执行业务调用,并由其负责输出响应,主线程退出
AsyncContext ctx = req.startAsync();
new Thread( new Executor( ctx ) ).start();
out.println( "结束Servlet的时间:" + new Date() + "." );
out.flush();
}
}
/*public class Executor implements Runnable {
private AsyncContext ctx = null;
public Executor(AsyncContext ctx) {
this.ctx = ctx;
}*/
class Executor implements Runnable {
private AsyncContext ctx = null;
public Executor(AsyncContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
try {
//等待十秒钟,以模拟业务方法的执行
Thread.sleep( 10000 );
PrintWriter out = ctx.getResponse().getWriter();
out.println( "业务处理完毕的时间:" + new Date() + "." );
out.flush();
ctx.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
pom.xml
4.0.0
Servlet4Push
Servlet4Push
1.0-SNAPSHOT
war
${project.build.directory}/endorsed
UTF-8
javax.servlet
javax.servlet-api
4.0.1
provided
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
${endorsed.dir}
org.apache.maven.plugins
maven-war-plugin
2.6
Servlet4Push
false
记得servlet4新功能:
**1、服务器推送:**是最直观的 HTTP/2 强化功能,通过 PushBuilder 接口在 servlet 中公开。服务器推送功能还在 JavaServer Faces API 中实现,并在 RenderResponsePhase 生命周期内调用,以便 JSF 页面可以利用其增强性能。
2、全新 servlet 映射发现接口: HttpServletMapping 使框架能够获取有关激活给定 servlet 请求的 URL 信息。这可能对框架尤为有用,这些框架需要这一信息来运行内部工作。
Servlet4.0优点:
服务器推送使服务器能预测客户端请求的资源需求。然后,在完成请求处理之前,它可以将这些资源发送到客户端。
要了解服务器推送的好处,可以考虑一个包含图像和其他依赖项(比如 CSS 和 JavaScript 文件)的网页。客户端发出一个针对该网页的请求。服务器然后分析所请求的页面,确定呈现它所需的资源,并主动将这些资源发送到客户端的缓存。
在执行所有这些操作的同时,服务器仍在处理原始网页请求。客户端收到响应时,它需要的资源已经位于缓存中。Servlet 4.0 通过 PushBuilder 接口公开服务器推送、也可以推送静态资源等。
代码实现:
Http2Servlet4.java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
//@WebServlet({"/path/*", "*.ext"})
@WebServlet(value = {"/http24"})
public class Http2Servlet4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PushBuilder pushBuilder = req.newPushBuilder();
if (pushBuilder != null) {
pushBuilder
.path("images/kodedu-logo.png")
.addHeader("content-type", "image/png")
.push();
}
/*PushBuilder pushBuilder = request.newPushBuilder();
if (pushBuilder != null) {
pushBuilder.path("images/hero-banner.jpg").push();
pushBuilder.path("css/menu.css").push();
pushBuilder.path("js/marquee.js").push();
}*/
HttpServletMapping mappings = req.getHttpServletMapping();
String mapping = mappings.getMappingMatch().name();
String value = mappings.getMatchValue();
String pattern = mappings.getPattern();
String servletName = mappings.getServletName();
try (PrintWriter respWriter = resp.getWriter();) {
respWriter.write("" +
"" +
"");
}
}
}
参考:https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/index.html
参考:https://www.ibm.com/developerworks/cn/java/j-javaee8-servlet4/index.html