现在的位置: 首页 > 综合 > 正文

【Servlet3.0新特性】第01节_Servlet注解及异步支持

2012年02月18日 ⁄ 综合 ⁄ 共 5946字 ⁄ 字号 评论关闭

这是一个Web Project

首先是web.xml(注意xml中的版本号和schema文件)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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-app_3_0.xsd">
	<!-- Servlet3.0需要Tomcat7和JavaEE6,配置方法详见 -->
	<!-- http://blog.csdn.net/jadyer/article/details/9164655 -->
	<!-- http://blog.csdn.net/jadyer/article/details/9164737 -->
</web-app>

下面是一个使用Servlet3.0注解实现的普通Servlet

package com.jadyer.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet3.0新特性之Servlet注解
 * @see 1)修改web.xml,将version改为3.0,将schema改为web-app_3_0.xsd
 * @see 2)在Servlet里面使用注解
 * @create Jun 23, 2013 5:46:18 PM
 * @author 玄玉<http://blog.csdn.net/jadyer>
 */
//urlPatterns--指定Servlet的访问URL(可以指定多个访问路径,因为urlPatterns的值类型是数组)
//initParams---设定初始参数,其值为WebInitParam类型的数组,所以它支持设定多个初始参数
//另外,@WebInitParam注解通常不单独使用,而是同@WebServlet或者@WebFilter组合使用
//而且,经过测试,单独使用@WebInitParam设定初始参数,设定失败
@WebServlet(urlPatterns={"/hello", "/servlet/hello"},
			initParams={@WebInitParam(name="savePath", value="D:/upload")})
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 2040116868162606895L;
	private String savePath;
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		//取得web.xml或者Servlet注解中initParams设定的值
		savePath = config.getInitParameter("savePath");
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		out.print("This is Servlet3.0 demo, and the savePath = " + this.savePath);
		//响应给请求方(本例中是浏览器)
		out.flush();
		out.close();
	}
}

下面是启用了异步支持的Servlet

package com.jadyer.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
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 com.jadyer.thread.HelloAsyncThread;

/**
 * Servlet3.0新特性之异步支持
 * @see -------------------------------------------------------------------------------------------------
 * @see 异步实现概述
 * @see 1)Servlet2.5中也可以单独启动一个线程去执行耗时的任务,接着Servlet会继续往下执行
 * @see   执行完最后一行代码时,Servlet就会把响应输出给请求方,而那个线程的任何执行结果都不会反映给请求方
 * @see 2)Servlet3.0中的异步支持也是单独启动线程执行耗时任务,当Servlet执行到最后一行代码时
 * @see   Servlet3.0提供了一些机制会判断那个线程是否执行完毕,直到线程执行完毕后才会响应所有结果给请求方
 * @see   (故本例中浏览器访问Servlet时,它会先转圈即正在加载...,直到5秒后异步线程执行完毕,浏览器才显示出了五行时间)
 * @see 3)若不单独启动线程去执行耗时任务,而是将耗时操作串行放到Servlet方法中执行的话,也是不可取的
 * @see   因为Servlet容器会管理着一个线程池,处理我们请求的Servlet所依附的线程都是来自于这个线程池的
 * @see   如果某个操作过于耗时,那么线程池里面的资源就会被占用掉,所以Servlet3.0就有了异步支持
 * @see -------------------------------------------------------------------------------------------------
 * @see 异步实现步骤
 * @see 1)在Servlet中开启异步支持(默认是不支持异步的),@WebServlet(asyncSupported=true)
 * @see 2)在Servlet中启动一个异步的上下文对象,AsyncContext context = request.startAsync();
 * @see 3)单独创建一个线程执行耗时操作,注意耗时操作执行完毕要调用AsyncContext.complete();
 * @see 4)在Servlet中启动耗时操作的线程
 * @see -------------------------------------------------------------------------------------------------
 * @create Jun 23, 2013 5:47:55 PM
 * @author 玄玉<http://blog.csdn.net/jadyer>
 */
@WebServlet(urlPatterns={"/hello/async"}, asyncSupported=true)
public class HelloAsyncServet extends HttpServlet {
	private static final long serialVersionUID = 3314056681141922826L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//获取当前方法的名字
		String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();

		PrintWriter out = response.getWriter();
		out.println(methodName + " starts: " + new Date());
		out.flush();
		System.out.println("第一步mark");
		
		//启动一个异步操作的上下文对象AsyncContext
		AsyncContext context = request.startAsync();
		//添加一个监听器(奇怪:AsyncListener竟然没有适配器)
		context.addListener(new AsyncListener(){
			@Override
			public void onComplete(AsyncEvent asyncEvent) throws IOException {
				PrintWriter out = asyncEvent.getSuppliedResponse().getWriter();
				out.println("async succes: " + new Date());
				out.flush();
				/**
				 * 在这里关闭流(此处为重点)
				 * @see -------------------------------------------------------------------------------------
				 * @see 总结:自打请求进入doGet()方法中开始,直到请求方收到响应为止
				 * @see     整个过程中的任意位置所获得的PrintWriter和ServletResponse对象都是相同的
				 * @see 说明:在5个步骤mark位置,均执行System.out.println(PrintWriter或ServletResponse对象);
				 * @see     我们会发现在五个mark位置的这俩对象的hashCode和内存地址都是相同的
				 * @see 并且:第一个mark处声明的PrintWriter对象不能在第二个mark处关闭,只能在第4个或第5个mark处关闭
				 * @see     否则异步支持就会失败
				 * @see 补充:第5个mark处的out.println()也会作为响应结果给请求方,这点要注意
				 * @see -------------------------------------------------------------------------------------
				 */
				out.close();
				System.out.println("第五步mark");
			}
			@Override
			public void onError(AsyncEvent arg0) throws IOException {}
			@Override
			public void onStartAsync(AsyncEvent arg0) throws IOException {}
			@Override
			public void onTimeout(AsyncEvent arg0) throws IOException {}
		});
		//也可以用这种方法启动异步线程
		//context.start(new HelloAsyncThread(context));
		new Thread(new HelloAsyncThread(context)).start();
		
		out.println(methodName + "   ends: " + new Date());
		out.flush();
		System.out.println("第二步mark");
	}
}

最后是异步支持用到的线程类

package com.jadyer.thread;

import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.AsyncContext;

/**
 * Servlet3.0新特性之异步支持用到的线程类
 * @create Jun 23, 2013 6:05:55 PM
 * @author 玄玉<http://blog.csdn.net/jadyer>
 */
public class HelloAsyncThread implements Runnable {
	private AsyncContext context;
	
	public HelloAsyncThread(AsyncContext context){
		this.context = context;
	}

	@Override
	public void run() {
		try {
			PrintWriter out = context.getResponse().getWriter();
			out.println("async starts: " + new Date());
			out.flush();
			System.out.println("第三步mark");
			
			//模拟耗时操作,让线程睡5s
			Thread.sleep(5000);
			
			out.println("async   ends: " + new Date());
			out.flush();
			System.out.println("第四步mark");
			
			//调用complete()表明异步处理已完成
			//它会让Servlet知道异步处理已完毕,否则Servlet还在傻傻等待,浪费时间
			context.complete();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

抱歉!评论已关闭.