第3章 深入Servlet技术
请求-响应模式就是典型的Web应用程序访问过程,Java Web应用程序中,处理请求并发送响应的过程是由一种叫做Servlet的程序来完成的。
请求request,响应response,与HTTP协议
Telnet演示HTTP协议
默认telnet关闭的,控制面板中,program and feathers,找到telnet client进行开启。
GET方式查询:常用来查询信息
HTTP头数据:
User-Ageent:Mozilla/4.0(...)   浏览器信息
Accept:text/html; */*              浏览器支持的格式
cookie: account-Helloween       记录用户当前的状态
Referer: http://www.baidu.com 指从哪个页面单击链接进入的
POST方式提交数据
GET提交数据不能超过256字符,POST提交,数据不在浏览器地址中显示,常用来提交表单数据
其他访问方式
HEAD,DELETE,TRACE,PUT,OPTIONS
Servlet概述
Java Web应用程序中所有的请求-响应都是Servlet完成的,没有main之类的方法,当用户访问服务器的时候,Tomcat通过调用Servlet的某些方法完成整个处理过程的。
Servlet工作流程
HttpServeltRequest/HttpServletResponse
Servlet接口
GET/POST/HEAD/PUT/DELETE/TRACE
一般为 doGet()/doPost() .../getLastModified(request)返回文档的最后修改时间
Java Web目录结构
Web程序部署在Tomcat的/webapps下面,用 http://localhost:8080/web1
编写Servlet
javax.servlet.* javax.servlet.http.*
一般直接继承HttpServlet,覆盖方法即可,一般只覆盖doGet(),doPost()
3.3.1 实现Servlet
编写Web Project,FirstServlet.java,覆盖常用三个方法
本书使用的是java EE5,新建项目时需要注意
新建web Project-> 新建servlet,
FirstServlet.java
/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred** GET方式访问页面执行该函数 执行doGet之前会执行getLastModified,如果发现返回的数值与上次访问是相同,* 则认为文档没有更新,浏览器采用缓存而不执行,如果返回 -1,则认为最新,总是执行该函数*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {this.log("执行doGet方法..." );// 控制台日志输出信息this.execute(request , response );// 处理doGet}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to post.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred**  以post方式访问页面时执行该函数,执行前不会调用getLastModified*/public void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {this.log("执行doPost方法" );this.execute(request , response );}@Overrideprotected long getLastModified(HttpServletRequest req) {// TODO Auto-generated method stubthis.log("执行 getLastModified 方法" );return -1;}// 执行方法private void execute(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {response.setCharacterEncoding( "UTF-8");request.setCharacterEncoding( "UTF-8");String requestURI = request.getRequestURI();String method = request.getMethod();String param = request.getParameter("param" );// 客户端提交的参数 param值response.setContentType( "text/html");// 文档类型为HTML类型PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( " <HEAD><TITLE>A Servlet</TITLE></HEAD>");out.println( " <BODY>");out.println( " 以" + method + " 方式访问该页面。取到的param参数为:" + param + "<br/>" );out.println( " <form action='"+ requestURI+ "' method='get'> <input type='text' name='param' value='param string'><input type='submit' value='以GET方式查询页面"+ requestURI + "'> </form>" );out.println( " <form action='"+ requestURI+ "' method='post'> <input type='text' name='param' value='param string'><input type='submit' value='以POST方式提交页面 "+ requestURI + "'></form>" );//客户端浏览器读取该文档的更新时间out.println( " <script>document.write('本页面最后更新时间: ' + docment.lastModified); </script>" );out.println( " </BODY>");out.println( "</HTML>");out.flush();out.close();}
3.3.2 配置<Servlet>
首先名称及类名:
<servlet-name> FirstServlet</servlet-name >
<servlet-class >servlet.FirstServlet </servlet-class><init-param><param-name >message </param-name><param-value >welcome to FisrtServlet </param-value></init-param ><init-param ><param-name >encoding </param-name><param-value >utf-8</ param-value></init-param >
配置一个初始化参数
< load-on-startup>1 </load-on-startup>
配置Servlet的加载方式。0和1,1启动加载,否则,Tomcat会在有人第一次请求加载
<servlet-mapping><servlet-name >FirstServlet </servlet-name><url-pattern >/ servlet/FirstServlet</url-pattern ></servlet-mapping >
<url-pattern > 配置该Servlet的访问方式:这里即http://localhost:8080/servlet /servlet/FirstServlet
如果配置为/servlet/FirstServlet.* 使用http://localhost:8080/servlet /servlet/FirstServlet.XXX访问
通配符试了好像不行,还是支持多个映射,把需要的映射加进去吧
一个完整的servlet包括servlet类,<servlet>配置,<servlet-mapping>配置。利用myeclipse向导自动完成配置。
???部署到Tomcat6.0系统,然后启动,发现超时,可以使用:
部署的时候出现Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor?
我们找到当前工程的workplace目录,然后按下面的操作:
找到workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml文件。
把其中的start-timeout="45" 改为  start-timeout="100" 或者更长,根据不同同学的工程大小来设置。
最后重启eclipse就可以了。
3.4 请求与响应
获取request变量
客户端浏览器发出的请求被封装HttpServletRequest对象,包含所有的信息。
在servlet项目中新建servlet,
RequestServlet.java:
public class RequestServlet extends HttpServlet {/*** Constructor of the object.*/public RequestServlet() {super();}/*** Destruction of the servlet. <br>*/public void destroy() {super.destroy(); // Just puts "destroy" string in log// Put your code here}// 返回客户端浏览器接受的文件类型private String getAccept(String accept) {StringBuffer buffer = new StringBuffer();if (accept .contains("image/gif"))buffer.append( "GIF文件,");if (accept .contains("image/x-xbitmap"))buffer.append( "BMP文件,");if (accept .contains("image/jpeg"))buffer.append( "JPG文件,");if (accept.contains("application/vnd.ms-execel" ))buffer.append( "EXCEL文件," );if (accept.contains("application/vnd.ms-powerpoint" ))buffer.append( "PPT文件,");if (accept .contains("application/vnd.msword"))buffer.append( "Word文件," );return buffer .toString().replaceAll(", $", "");}// 返回客户端的语言环境private String getLocale(Locale locale) {if (Locale.SIMPLIFIED_CHINESE.equals(locale))return "简体中文" ;if (Locale.TRADITIONAL_CHINESE.equals(locale))return "繁体中文" ;if (Locale.ENGLISH.equals(locale))return "英文" ;if (Locale.JAPANESE.equals(locale))return "日文" ;return "未知语言环境" ;}// 返回IP地址对应的物理地址// private String getAddress(String ip){// return IpUtil.getIpAddress(ip);// }// 返回客户端浏览器信息private String getNavigatior(String userAgent) {if (userAgent .indexOf("TencentTraveler") > 0)return "腾讯浏览器" ;if (userAgent .indexOf("Maxthon") > 0)return "Maxthon浏览器" ;if (userAgent .indexOf("MyIE2") > 0)return "MyIE2浏览器" ;if (userAgent .indexOf("Firefox") > 0)return "Firefox浏览器" ;if (userAgent .indexOf("MSIE") > 0)return "IE浏览器" ;return "未知浏览器" ;}// 返回客户端操作系统private String getOS(String userAgent) {if (userAgent .indexOf("Windows NT 5.1") > 0)return "Windows XP" ;if (userAgent .indexOf("Windows 98") > 0)return "Windows 98" ;if (userAgent .indexOf("Windows NT 5.0") > 0)return "Windows 2000" ;if (userAgent .indexOf("Linux") > 0)return "Linux" ;if (userAgent .indexOf("Unix") > 0)return "Unix" ;return "未知" ;}/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {request.setCharacterEncoding( "UTF-8");response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html");//文档类型为HTMLString authType = request .getAuthType();String localAddr = request .getLocalAddr();//本地IP,服务器IPString localName = request .getLocalName();//本地名称,服务器名称int localPort = request .getLocalPort();//本地端口,Tomcat端口Locale locale = request.getLocale(); //用户的语言环境String contextPath = request .getContextPath();String method = request .getMethod();//get还是postString pathInfo = request .getPathInfo();String pathTranslated = request.getPathTranslated();String protocol = request.getProtocol(); //协议,这里指HTTP协议String queryString = request.getQueryString();//查询字符串,即?后面的字符串String remoteAddr = request .getRemoteAddr();//远程IP,客户端IPint port = request .getRemotePort();//远程端口,客户端端口String remoteUser = request .getRemoteUser();//远程用户String requestedSessionId = request.getRequestedSessionId();//客户端session的IDString requestURI = request .getRequestURI();//用户请求的URIStringBuffer requestURL = request.getRequestURL();//用户请求的URLString scheme = request.getScheme(); //协议头,这里为 httpString serverName = request .getServerName();//服务器名称int serverPort = request .getServerPort();//服务器端口String servletPath = request.getServletPath();// servlet的路径Principal userPrincipal = request.getUserPrincipal();String accept = request.getHeader( "accept");//浏览器支持的格式String referer = request.getHeader("referer" );//从哪个页面单击到本页//user-agent 包括操作系统类型、版本号、浏览器类型、版本号String userAgent = request.getHeader("user-agent" );String serverInfo = this.getServletContext().getServerInfo();PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>Request Servlet</TITLE></HEAD>");out.println( " <style>body, font, td, dic {font-size:12px; line-height:18px }</style>");out.println( "  <BODY>");out.println( "<b>您的IP为</b>" + remoteAddr + "<b>,位于</b>:空 "+ "<b>; 您使用</b>" + getOS(userAgent) + "<b>操作系统</b>,"+ getNavigatior( userAgent) + "<b>,您使用</b> " + getLocale(locale)+ "。 <br/>");out.println( "<b>服务器IP为</b>" + localAddr + "<b>,位于</b>:空 "+ "<b>; 服务器使用</b>" + serverPort + "<b>端口,您的浏览器使用了</b>," + port+ "<b>端口访问本网页。</b><br/>" );out.println( "<b>服务器软件为</b>" + serverInfo + "<b>服务器名称为</b>  "+ localName + "。<br/>" );out.println( "<b>您的浏览器接受</b>" + getAccept(accept) + "。<br/>" );out.println( "<b>您从</b>" + referer + "<b>访问到该页面。</br><br/>" );out.println( "<b>使用的协议为</b>" + protocol + "。<b>URL协议头</b>" + scheme+ ",<b>服务器名称</b>" + serverName + ",<b>您访问的URI为</b>"+ requestURI + "。<br/>" );out.println( "<b>该servlet的路径为</b>" + servletPath + ", <b>该servlet类名为</b>" + this.getClass().getName()+ "。<br/>");out.println( "<b>本应用程序在硬盘的根目录为</b>" + this.getServletContext().getRealPath("" ) + ", <b>网络的相对路径为</b>" + contextPath + "。<br/>" );out.println( "<br/>");out.println( "<br/><br/><a href=" + requestURI + "> 单击刷新本页面</a>" );out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}/*** Initialization of the servlet. <br>** @throws ServletException*             if an error occurs*/public void init() throws ServletException {// Put your code here}
}
结果:
3.4.2 response生成图片验证码
通过HttpServletResponese获取的PrintWrite对象只能写字符型数据。
这里使用Servlet输出图片验证码,原理:
服务器生成一个包含随机字符串的图片发给客户端,客户端提交数据时需要填写字符串作为验证。
Servlet输出图片时,需要调用getOutputStream.
IdentityServlet.java
public class IdentityServlet extends HttpServlet {// 随机字典 不包括男人的0,O,1,Ipublic static final char[] CHARS = { '2', '3', '4', '5', '6' , '7' , '8' ,'9', 'A', 'B', 'C', 'D', 'D', 'E', 'F', 'G', 'H' , 'J' , 'K' , 'L' ,'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' , 'X' , 'Y' , 'Z' };public static Random random = new Random();// 获取6位随机数public static String getRandomstring() {StringBuffer buffer = new StringBuffer();// 字符串缓存for (int i = 0; i < 6; i++) {buffer.append( CHARS[random .nextInt(CHARS. length)]);}return buffer .toString();}// 获取随机的颜色public static Color getRandomColor() {return new Color(random.nextInt(255), random.nextInt(255),random.nextInt(255));}// 返回某颜色的反色public static Color getReverseColor(Color c ) {return new Color(255 - c.getRed(), 255 - c.getGreen(),255 - c.getBlue());}/*** Constructor of the object.*/public IdentityServlet() {super();}/*** Destruction of the servlet. <br>*/public void destroy() {super.destroy(); // Just puts "destroy" string in log// Put your code here}/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setContentType( "image/jpeg");// 设置输出类型String randomString = getRandomstring();// 放到session中request.getSession( true).setAttribute("randomString" , randomString);// 图片高度和宽度int width = 100;int height = 30;// 随机颜色 背景色Color color = getRandomColor();Color reverse = getReverseColor(color);// 创建彩色图片BufferedImage bi = new BufferedImage(width , height,BufferedImage. TYPE_INT_RGB);// 获取绘图对象Graphics2D g = bi.createGraphics();g.setFont( new Font(Font.SANS_SERIF, Font.BOLD, 16));g.setColor( color);g.fillRect(0, 0, width, height); // 绘制背景g.setColor( reverse); // 设置颜色g.drawString( randomString, 18, 20);// 绘制随机字符for (int i = 0, n = random.nextInt(100); i < n; i++) {g.drawRect( random.nextInt(width ), random.nextInt(height ), 1, 1);}ServletOutputStream out = response.getOutputStream();JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);encoder.encode(bi);out.flush();}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to* post.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>A Servlet</TITLE></HEAD>");out.println( "  <BODY>");out.print( "    This is ");out.print( this.getClass());out.println( ", using the POST method");out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}/*** Initialization of the servlet. <br>** @throws ServletException*             if an error occurs*/public void init() throws ServletException {// Put your code here}}
注意
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(bi);
需要导入头文件:
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
生成的结果如下:
我们可以html.文件引用这个图片,identity.html
注意html中的路径问题;
<script>function reloadImage() {document.getElementById( 'btn').disabled = true;document.getElementById( 'identity').src = 'http://localhost:8080/servlet/servlet/IdentityServlet?ts='+ new Date().getTime();}
</ script>

<img src="http://localhost:8080/servlet/servlet/IdentityServlet" id="identity" οnlοad= "btn.disabled = false;" />
<input type= button value ="换个图片" οnclick="reloadImage()" id="btn">
生成的结果如下:
???Several ports (8005, 8080, 8009) required by Tomcat v6.0 Server at localhost are already in use. The server may already be running in another process, or a system process may be using the port. To start this server you will need to stop the other process or change the port number(s).
到任务管理器中把tomcat的运行关闭,就是java的进程
3.5 读取 web.xml参数
如果需求变化,修改源代码,重新编译class文件,然后重新部署。
常量信息更倾向于写在某个配置文件中。
3.5.1 初始化参数
web.xml配置初始化参数后,Servlet中提供方法getInitParameter(String param)获取初始化值
这些初始化参数也可以有ServletConfig对象取得。
eg
notice.html放在/WEB-INF中,受保护的,通过程序读取
首先三个初始化参数
 <init-param ><param-name >halloween</param-name><param-value >password </param-value></init-param ><init-param ><param-name >admin</param-name><param-value >admin</param-value></init-param ><init-param ><param-name >babyface</param-name><param-value >babyface</param-value></init-param >
然后InitParamServlet.java
public class InitParamServlet extends HttpServlet {/*** Constructor of the object.*/public InitParamServlet() {super();}/*** Destruction of the servlet. <br>*/public void destroy() {super.destroy(); // Just puts "destroy" string in log// Put your code here}/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {request.setCharacterEncoding( "UTF-8");response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>请登录查看 Notice文件</TITLE></HEAD>" );out.println( "<style> body, td, div {font-size:12px}</style>");out.println( "  <BODY>");out.println( "<form action='" + request.getRequestURI() + "'method='post'>" );out.println( "账号:<input type='text' name='username' style='width:200px;'> </br>" );out.println( "密码:<input type='password' name='password' style='width:200px;'> <br/><br/>");out.println( "<input type='submit' value=' 登录  '>");out.println( "</form>");out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to post.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {//提交的username/password参数String username = request.getParameter("username" );String password = request.getParameter("password" );//所有的初始化参数名称Enumeration params = this.getInitParameterNames();//遍历所有的初始化参数while(params .hasMoreElements()){//参数名 即用户名  参数值 即密码String usernameParam = (String)params.nextElement();String passnameParam = getInitParameter(usernameParam );//用户名 密码匹配if(usernameParam .equalsIgnoreCase(username) && passnameParam.equals(password )){request.getRequestDispatcher( "/WEB-INF/notice.html").forward(request ,response );return ;}}this.doGet(request , response );//不匹配 ,显示登录界面}/*** Initialization of the servlet. <br>** @throws ServletException if an error occurs*/public void init() throws ServletException {// Put your code here}
}
结果如下:登录后跳转到notice.html
3.5.2  上下文参数
由于init-param在servlet中,所以不是全局参数,不能被其他servlet读取
配置所有的Servlet都能读取的,上下文参数使用标签<context-param>
 <context-param ><param-name >upload folder </param-name><param-value >attachment </param-value></context-param ><context-param ><param-name >allowed file type </param-name><param-value >. gif,.jpg ,.bmp</param-value ></context-param >
获取context-param可以使用ServletContext对象。
Servlet中通过getServletConfig(),getServletContext()获取
ContextParamServlet.java
public class ContextParamServlet extends HttpServlet {/*** @see HttpServlet#HttpServlet()*/public ContextParamServlet() {super();// TODO Auto-generated constructor stub}/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println("  <HEAD><TITLE>读取上下文参数</TITLE></HEAD>");
out.println("  <link rel='stylesheet' type='text/css' href='../css/style.css'>");
out.println("  <BODY>");
out.println("<div align=center><br/>");
out.println("<fieldset style='width:90%'><legend>所有的上下文参数</legend><br/>");
//获取上下文
ServletContext servletContext = getServletConfig().getServletContext();
String uploadFoder = servletContext.getInitParameter("upload folder");
String allowedFileType = servletContext.getInitParameter("allowed file type");
out.println("<div class='line'>");
out.println("  <div align='left' class='leftDiv'>上传文件夹</div>");
out.println("  <div align='left' class='rightDiv'>" + uploadFoder + "</div>");
out.println("</div>");
out.println("<div class='line'>");
out.println("  <div align='left' class='leftDiv'>实际磁盘路径</div>");
out.println("  <div align='left' class='rightDiv'>" + servletContext.getRealPath(uploadFoder) + "</div>");
out.println("</div>");
out.println("<div class='line'>");
out.println("  <div align='left' class='leftDiv'>允许上传的类型</div>");
out.println("  <div align='left' class='rightDiv'>" + allowedFileType + "</div>");
out.println("</div>");
out.println("</fieldset></div>");
out.println("  </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}}
结果如下:

3.5.3 资源注射
不需要Servlet主动去读取资源,Tomcat启动时候会把web.xml里配置的信息主动注射到Servlet中。
@Resource(name=“messageNameInWebXml”)
private String message;
使用@Resource标注字符串message,表示message会在Servlet运行时动态注入,
然后再web.xml中配置一个名为messageNameInWebXml参数。
实例如下:InjectionServlet.java
public class InjectionServlet extends HttpServlet {private @Resource(name="hello" ) String hello;//注入的字符串private @Resource(name="i" ) int i;//注入的整数@Resource(name= "persons")//两行的写法private String persons;//注解与代码分开/*Context ctx = new InitialContext();String message = (String)ctx.lookup("message");Integer i =(Integer)ctx.lookup("i");String persons = (String)ctx.lookup("persons");*//*** Constructor of the object.*/public InjectionServlet() {super();}/*** Destruction of the servlet. <br>*/public void destroy() {super.destroy(); // Just puts "destroy" string in log// Put your code here}/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setContentType( "text/html");response.setCharacterEncoding( "UTF-8");request.setCharacterEncoding( "UTF-8");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>资源注入</TITLE></HEAD>" );out.println( "<style>body {font-size=12px; }</style>");out.println( "<b>注入的字符串</b>: <br/>  - " + hello + "<br/>");out.println( "<b>注入的整数</b>: <br/>  - " + i + "</br>");out.println( "<b>注入的字符数组</b>:<br/>" );for(String person :persons .split("," )){out.println( "  - " + person +"<br/>");}out.println( "  <BODY>");out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}/*** Initialization of the servlet. <br>** @throws ServletException if an error occurs*/public void init() throws ServletException {// Put your code here}
}
web.xml中使用标签<env-envy>来配置资源
<servlet><description >This is the description of my J2EE component</description><display-name >This is the display name of my J2EE component</display-name><servlet-name >InjectionServlet </servlet-name><servlet-class> servlet.InjectionServlet</servlet-class ></servlet >
<servlet-mapping><servlet-name >InjectionServlet </servlet-name><url-pattern >/ servlet/InjectionServlet</url-pattern ></servlet-mapping ><!-- 下面是资源配置 --><env-entry ><env-entry-name >hello </env-entry-name><env-entry-type >java.lang.String </env-entry-type><env-entry-value >Hello, Welcome to the JavaEE Resource Injection.</env-entry-value></env-entry ><env-entry ><env-entry-name >i </env-entry-name><env-entry-type >java.lang.Integer </env-entry-type><env-entry-value >30 </env-entry-value></env-entry ><env-entry ><env-entry-name >persons </env-entry-name><env-entry-type >java.lang.String </env-entry-type><env-entry-value >Helloween, Cobain, Rose, Axi,</env-entry-value ></env-entry >
程序的运行效果如:

3.5.4 使用JNDI获取资源
Context ctx = new InitialContext();
String message = (String)ctx.lookup("message");
Integer i =(Integer)ctx.lookup("i");
String persons = (String)ctx.lookup("persons");
3.6 提交表单信息
客户端提交的信息可能来自表单里的文本框、密码框、文件域等,以参数形式提交到服务器。
Servlet的任务就是正确的获取这些信息,并根据信息做不同的响应。
提交的信息包括get/post,GET用于从服务器获取信息,Post用于向服务器提交信息。
POST提交信息包括:普通POST提交方式和可以上传文件的POST提交方式。
3.6.1GET实现搜素引擎
HTML使用FORM提交数据,action属性设置将数据提交到哪个URL。
GET方式提交,变量之间&连接,Servlet路径+问号?+查询字符串的形式获取服务器内容。
GET提交的典型方式就是搜索引擎。
新建search.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title> search.html</title >
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style> div, body, span {font-size:14px; } </style>
</head><body>
<div align= "center"><img src="data:image/yahoo.gif" style=' margin: 25px; '><div ><form action= '/servlet/servlet/SearchServlet' method='get' ><input type= "radio" name ="type" value="web" checked>网页<input type= "radio" name ="type" value="news" >新闻<input type= "radio" name ="type" value="image" >图片<input type= "radio" name ="type" value="video" >视频       <input type= "checkbox" name ="allowedAdult" value="true" >允许成人内容 <br/>< br/><input type= "text" name ="word" value="" style="width :300px; "> <input type="submit" value= "用雅虎搜索" style=" width: 100px; "></form></div ><div style=" margin-top:50px ; ">&copy; Helloween 2007-2010</div >
</div></body>
</html>
然后写一个searchServlet实现关键的业务逻辑部分:搜索引擎程序。
这里使用Yahoo API,所以要加yahoo_search-2.0.1.jar加入到库中,即/WEB-INF/lib
SearchServlet.java
public class SearchServlet extends HttpServlet {/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setCharacterEncoding( "UTF-8");request.setCharacterEncoding( "UTF-8");// 搜索关键字String word = request.getParameter( "word");// 搜索类型String type = request.getParameter( "type");// 是否允许成人内容。如果选中,则为 "true",否则为 null.String allowedAdult = request.getParameter("allowedAdult" );boolean adultOk = "true" .equals(allowedAdult );response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>" + word + " 搜索结果</TITLE></HEAD>" );out.println( "<style>");out.println( " body, div {font-size:12px; padding:2px; margin:0px; }");out.println( " .imgDiv{float:left; width: 172px; height:250px;  margin:2px; padding:2px; border:1px pink solid; overflow:hidden; }");out.println( "</style>");out.println( "  <BODY>");out.println( "<div style='float:left; height:40px; '><img src='../images/yahoo.gif'></div>");out.println( "<form action='" + request.getRequestURI() + "' method='get'>" );out.println( " <div style='height:40px; '>" );out.println( "      <input type='radio' name='type' value='web' " + (type.equals("web" )?"checked" :"" ) + ">网页" );out.println( "      <input type='radio' name='type' value='news' " + (type.equals("news" )?"checked" :"" ) + ">新闻" );out.println( "      <input type='radio' name='type' value='image' " + (type.equals("image" )?"checked" :"" ) + ">图像" );out.println( "      <input type='radio' name='type' value='video' " + (type.equals("video" )?"checked" :"" ) + ">视频" );out.println( "            " );out.println( "      <input type='checkbox' name='allowedAdult' value='true' " + (adultOk?"checked" :"" ) + ">允许成人内容 <br/>" );out.println( "      <input type='text' name='word' value='" + word + "' style='width:300px; '> <input type='submit' value='用雅虎搜索' style='width:100px; '>");out.println( " </div>");out.println( "</form>");SearchClient client = new SearchClient("javasdktest" );try{if("image" .equals(type )){ImageSearchRequest searchRequest = new ImageSearchRequest(URLEncoder.encode( word, "UTF-8"));// 是否显示成人内容searchRequest.setAdultOk(adultOk );// 查询记录数searchRequest.setResults(20);// 从第 0 条记录开始显示searchRequest .setStart(BigInteger.valueOf (0));double startTime = System.currentTimeMillis();ImageSearchResults results = client.imageSearch(searchRequest );double endTime = System.currentTimeMillis();out.println( "<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>");out.println( " 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。");out.println( "</div>");for(ImageSearchResult result : results.listResults()){out.println( "<div class='imgDiv'>");out.println( " <div align='center'><a href=\"" + result.getClickUrl() + "\" target=_blank><img width=160 height=120 src=\"" + result.getThumbnail().getUrl() + "\" border='0'></a></div>");out.println( " <div align='center'><a href=\"" + result.getRefererUrl() + "\" target=_blank>" + result.getTitle() + "</a></div>" );out.println( " <div align='center'>" + result.getWidth() + "x" + result .getHeight() + " " + result.getFileFormat() + "</div>" );out.println( " <div>" + (result.getSummary()==null ? "" : result.getSummary()) + "</div>");out.println( "</div>");}}else if ("web" .equals(type )){WebSearchRequest searchRequest = new WebSearchRequest(URLEncoder.encode(word, "UTF-8"));// 是否显示成人内容searchRequest.setAdultOk(adultOk );// 查询记录数searchRequest.setResults(20);// 从第 0 条记录开始显示searchRequest .setStart(BigInteger.valueOf (0));double startTime = System.currentTimeMillis();WebSearchResults results = client.webSearch(searchRequest );double endTime = System.currentTimeMillis();out.println( "<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>");out.println( " 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。");out.println( "</div>");for(WebSearchResult result : results.listResults()){out.println( "<div style='margin:8px; width:500px; '>");out.println( " <div><a href=\"" + result.getClickUrl() + "\" target=_blank><b>" + result.getTitle() + "</b></a> 文件格式:" + result.getMimeType() + "</div>" );out.println( " <div>网址:<a href=\"" + result.getUrl() + "\" target=_blank>" + result .getUrl() + "</a></div>" );out.println( " <div>" + result.getSummary() + (result .getCache()==null ? "" : " [<a href=\"" + result.getCache().getUrl() + "\" target=_blank>网页快照</a>]") + "</div>");out.println( "</div>");}}else if ("news" .equals(type )){NewsSearchRequest searchRequest = new NewsSearchRequest(URLEncoder.encode( word, "UTF-8"));// 是否显示成人内容
//             searchRequest.setAdultOk(adultOk);// 查询记录数searchRequest.setResults(20);// 从第 0 条记录开始显示searchRequest .setStart(BigInteger.valueOf (0));double startTime = System.currentTimeMillis();NewsSearchResults results = client.newsSearch(searchRequest );double endTime = System.currentTimeMillis();out.println( "<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>");out.println( " 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。");out.println( "</div>");for(NewsSearchResult result : results.listResults()){out.println( "<div style='margin:8px; width:500px; '>");out.println( " <div><a href=\"" + result.getClickUrl() + "\" target=_blank><b>" + result.getTitle() + "</b></a></div>" );out.println( " <div>网址:<a href=\"" + result.getUrl() + "\" target=_blank>" + result .getUrl() + "</a></div>" );out.println( " <div>" + result.getSummary() + "</div>" );out.println( "</div>");}}else if ("video" .equals(type )){VideoSearchRequest searchRequest = new VideoSearchRequest(URLEncoder.encode( word, "UTF-8"));// 是否显示成人内容searchRequest.setAdultOk(adultOk );// 查询记录数searchRequest.setResults(20);// 从第 0 条记录开始显示searchRequest .setStart(BigInteger.valueOf (0));double startTime = System.currentTimeMillis();VideoSearchResults results = client.videoSearch(searchRequest );double endTime = System.currentTimeMillis();out.println( "<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>");out.println( " 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。");out.println( "</div>");for(VideoSearchResult result : results.listResults()){out.println( "<div class='imgDiv'>");out.println( " <div align='center'><a href=\"" + result.getClickUrl() + "\" target=_blank><img width=160 height=120 src=\"" + result.getThumbnail().getUrl() + "\" border='0'></a></div>");out.println( " <div align='center'><a href=\"" + result.getRefererUrl() + "\" target=_blank>" + result.getTitle() + "</a></div>" );out.println( " <div align='center'>" + result.getWidth() + "x" + result.getHeight() + " " + result .getFileFormat() + "</div>");out.println( " <div>" + (result.getSummary()==null ? "" : result.getSummary()) + "</div>");out.println( "</div>");}}} catch(Exception e ){e.printStackTrace();out.println( "<font color=red>Exception: " + e.getMessage() + "</font>" );}out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}
}
运行,直接访问http://localhost:8080/servlet/servlet/SearchServlet是不行的:

HTTP Status 500 -


type Exception report

message

description The server encountered an internal error that prevented it from fulfilling this request.

exception

java.lang.NullPointerExceptionservlet.searchServlet.doGet(searchServlet.java:64)javax.servlet.http.HttpServlet.service(HttpServlet.java:617)javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
note The full stack trace of the root cause is available in the Apache Tomcat/6.0.44 logs.
直接访问肯定是不对的。
首先键入正确的search.html的地址如下:进行搜索
跳转到SearchServlet的页面
出现如上结果,有可能yahoo的API问题,不管了,只需要留意地址栏的搜索内容。
如果想在该搜索引擎搜中文,需要修改Tomcat目录下的\conf\server.xml里设定的默认的GET编码方式,
<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>
GET提交表单时,提交的内容显示在地址栏中,并会被记录在缓存中,因此提交敏感的信息不能使用GET,
并且GET提交URL总长度不能超过255个字符,因此提交过长的内容时也不能使用GET。
3.6.2 POST提交个人信息
把HTML中的FORM的method设置为POST。
新建postPersonalInformation.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title> 提交用户信息</title >
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style>
body, div, td, input { font-size:12px ; margin :0px; }
select {height :20px; width:300px; }
.title {font-size : 16px; padding: 10px; margin: 10px; width:80% ; }
.text {height :20px; width:300px; border: 1px solid #AAAAAA; }
.line {margin :2px; }
.leftDiv {width :110px; float:left; height: 22px; line-height:22px ; font-weight :bold; }
.rightDiv {height :22px; }
.button {color:#fff;font-weight: bold;font-size: 11px;text-align:center;padding:.17em 0 .2em .17em;border-style: solid;border-width: 1px;border-color: #9cf #159 #159 #9cf;background:#69c url(images/bg-btn-blue.gif) repeat-x;
}
</style>
</head>
<body>
<form action= "/servlet/servlet/PostServlet" method="POST" >
<div align= "center"><br /><fieldset style='width:90%'><legend> 填写用户信息</legend ><br/><div class= 'line'><div align= "left" class ='leftDiv'>请填写您的姓名:</div ><div align= "left" class ='rightDiv'><input type= "text" name ="name" class="text" /></div></div><div class= 'line'><div align= "left" class ='leftDiv'>请填写您的密码:</div ><div align= "left" class ='rightDiv'><input type= "password" name ="password" class="text" /></div></div><div class= 'line'><div align= "left" class ='leftDiv'>请再次输入密码:</div ><div align= "left" class ='rightDiv'><input type= "password" name="passwordConfirm" class="text" /></div></div><div class= 'line'><div align= "left" class ='leftDiv'>请选择性别:</div ><div align= "left" class ='rightDiv'><input type= "radio" name ="sex" value="男" id="sexMale"><label for= "sexMale">男 </label><input type= "radio" name ="sex" value="女" id="sexFemale"><label for= "sexFemale">女 </label></div></div><div class= 'line'><div align= "left" class ='leftDiv'>请输入年龄:</div ><div align= "left" class ='rightDiv'><input type= "text" name ="age" class="text" ></div></div><div class= 'line'><div align= "left" class ='leftDiv'>请输入生日:</div ><div align= "left" class ='rightDiv'><input type= "text" name ="birthday" class="text" ><br/> 格式:"yyyy -mm-dd"</div></div><div class= 'line'><div align= "left" class ='leftDiv'>请选择您的爱好</div ><div align= "left" class ='rightDiv'><input type= "checkbox" name="interesting" value="音乐影视" id ="i1"><label for= "i1">音乐影视 </label><input type= "checkbox" name="interesting" value="外出旅游" id ="i2"><label for= "i2">外出旅游 </label><input type= "checkbox" name="interesting" value="社交活动" id ="i3"><label for= "i3">社交活动 </label></div></div><div class= 'line'><div align= "left" class ='leftDiv'>请选择省市:</div ><div align= "left" class ='rightDiv'><select name= "area"><option> ---请选择省份---</option ><optgroup label= "北京市" ><option value= "北京市海淀区" >海淀区 </option><option value= "北京市朝阳区" >朝阳区 </option><option value= "北京市东城区" >东城区 </option><option value= "北京市西城区" >西城区 </option></optgroup><optgroup label= "山东省" ><option value= "山东省济南市" >济南市 </option><option value= "山东省青岛市" >青岛市 </option><option value= "山东省潍坊市" >潍坊市 </option></optgroup></select></div></div><div class= 'line'><div align= "left" class ='leftDiv'>自我描述:</div ><div align= "left" class ='rightDiv'><textarea name= "description" rows ="8" style="width :300px; ">请填写其他资料... </textarea></div></div><div class= 'line'><div align= "left" class ='leftDiv'></div><div align= "left" class ='rightDiv'><br/>< input type ="submit" name= "btn" value=" 提交信息 " class="button">< br/></div></div></fieldset >
</div>
</form>
</body>
</html>
如下所示:
再编写一个Servlet接收HTML页面提交的信息,
PostServlet.java
public class PostServlet extends HttpServlet {/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setCharacterEncoding( "UTF-8");response.getWriter().println( "请使用 POST 方式提交数据。" );}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to post.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setCharacterEncoding( "UTF-8");request.setCharacterEncoding( "UTF-8");// 从 文本框 text 中取姓名String name = request.getParameter( "name");// 从 密码域 password 中取密码String password = request.getParameter("password" );// 从 单选框 checkbox 中取性别String sex = request.getParameter( "sex");int age = 0;try {// 取 年龄. 需要把 字符串 转换为 int.// 如果格式不对会抛出 NumberFormattingExceptionage = Integer.parseInt(request.getParameter( "age"));} catch (Exception e ) {}Date birthday = null;try {// 取 生日. 需要把 字符串 转化为 Date.// 如果格式不对会抛出 ParseExceptionDateFormat format = new SimpleDateFormat("yyyy-MM-dd" );birthday = format.parse(request .getParameter("birthday"));} catch (Exception e ) {}// 从 多选框 checkbox 中取多个值String[] interesting = request.getParameterValues("interesting" );// 从 下拉框 select 中取值String area = request .getParameter("area");// 从 文本域 textarea 中取值String description = request.getParameter("description" );// 取 提交按钮 的键值String btn = request.getParameter( "btn");response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "<HEAD><TITLE>感谢您提交信息</TITLE>" );out.println( "<style>");out.println( "body, div, td, input {font-size:12px; margin:0px; }");out.println( ".line {margin:2px; }");out.println( ".leftDiv {width:110px; float:left; height:22px; line-height:22px; font-weight:bold; }");out.println( ".rightDiv {height:22px; line-height:22px; }");out.println( ".button {");out.println( " color:#fff;");out.println( " font-weight:bold;");out.println( " font-size: 11px; ");out.println( " text-align:center;");out.println( " padding:.17em 0 .2em .17em;" );out.println( " border-style:solid;");out.println( " border-width:1px;");out.println( " border-color:#9cf #159 #159 #9cf;");out.println( "    background:#69c url(/servlet/images/bg-btn-blue.gif) repeat-x;");out.println( "</style>");out.println( "</HEAD>");out.println( "<div align=\"center\"><br/>" );out.println( "<fieldset style='width:90%'><legend>填写用户信息</legend><br/>");out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>您的姓名:</div>");out.println( "           <div align='left' class='rightDiv'>" + name + "</div>");out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>您的密码:</div>");out.println( "           <div align='left' class='rightDiv'>" + password+ "</div>");out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>您的性别:</div>");out.println( "           <div align='left' class='rightDiv'>" + sex + "</div>");out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>您的年龄:</div>");out.println( "           <div align='left' class='rightDiv'>" + age + "</div>");out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>您的生日:</div>");out.println( "           <div align='left' class='rightDiv'>");out.println( new SimpleDateFormat("yyyy年MM月dd日" ).format(birthday));out.println( "           </div>" );out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>您的兴趣:</div>");out.println( "           <div align='left' class='rightDiv'>");if (interesting != null)for (String str : interesting ) {out.println( str + ", ");}out.println( "           </div>" );out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>自我描述:</div>");out.println( "           <div align='left' class='rightDiv'>" + description+ "</div>");out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>按钮键值:</div>");out.println( "           <div align='left' class='rightDiv'>" + btn + "</div>");out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'></div>");out.println( "           <div align='left' class='rightDiv'>");out.println( "                   <br/><input type='button' name='btn' value='返回上一页' οnclick='history.go(-1); ' class='button'><br/>" );out.println( "           </div>" );out.println( "      </div>" );out.println( "<BODY>");out.println( "</BODY>");out.println( "</HTML>");out.flush();out.close();}
}
提交如下页面,出现错误:
查看出错原因,主要是:没注意生日的格式
重新填写如下:
接受到信息如下:
3.6.3 上传文件客户端
上传文件需要设置FORM的enctype属性为multipart/form-data,由于上传文件比较大,
需要设置该参数指定浏览器使用二进制上传,
如果不设置,entype默认为:application/x-www-form-urlencoded,使用ASCII发送,导致发送失败
上传文件需要使用文件域(<input type="file"/>),并把FORM的Enctype设置为multipart/form-data。
upload.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title> 上传文件</title >
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3" >
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel= "stylesheet" type ="text/css" href="css/style.css" >
</head>
<body>
<form action= "servlet/UploadServlet" method="post" enctype="multipart/form-data" >
<div align= "center"><br /><fieldset style=" width: 90%"><legend> 上传文件</legend ><br/><div class= 'line'><div align= 'left' class ="leftDiv"> 上传文件一</div ><div align= 'left' class ="rightDiv"><input type= "file" name ="file1" class="text" ></div></div><div class= 'line'><div align= 'left' class ="leftDiv"> 上传文件二</div ><div align= 'left' class ="rightDiv"><input type= "file" name ="file2" class="text" ></div></div><div class= 'line'><div align= 'left' class ="leftDiv"> 上传文件说明一 </div><div align= 'left' class ="rightDiv">< input type="text" name="description1" class= "text"></div ></div><div class= 'line'><div align= 'left' class ="leftDiv"> 上传文件说明二 </div><div align= 'left' class ="rightDiv">< input type="text" name="description2" class= "text"></div ></div><div class= 'line'><div align= 'left' class ="leftDiv"></ div><div align= 'left' class ="rightDiv">< br/><input type= "submit" value ="  上传文件  " class="button"></div></div></fieldset >
</div>
</form>
</body>
</html>
结果如下:
服务端比较复杂,解析而进程数据流比较麻烦。
SmartUpload类库,解析request过程中 放于内存中,速度快,但较大文件会发生溢出。
Commons FileUpload框架:
首先将类库的jar文件加入项目中
新建UploadServlet.java
public class UploadServlet extends HttpServlet {/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setContentType( "text/html");response.getWriter().println( "请以POST方式上传文件" );}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to post.** @param request the request send by the client to the server* @param response the response send by the server to the client* @throws ServletException if an error occurred* @throws IOException if an error occurred*/@SuppressWarnings( "unchecked")  //消除unchecked warningpublic void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {File file1 = null, file2 = null;String description1 = null , description2 = null;response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>A Servlet</TITLE></HEAD>");out.println( "  <link rel='stylesheet' type='text/css' href='../css/style.css'>");out.println( "  <BODY>");out.println( "<div align=center><br/>" );out.println( "<fieldset style='width:90%'><legend>上传文件</legend><br/>");out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>上传日志:</div>");out.println( "           <div align='left' class='rightDiv'>");// 使用 DiskFileUpload 对象解析 requestDiskFileUpload diskFileUpload = new DiskFileUpload();try {// 将 解析的结果 放置在 List 中List<FileItem> list = diskFileUpload.parseRequest(request);out.println( "遍历所有的 FileItem ... <br/>");// 遍历 list 中所有的 FileItemfor(FileItem fileItem : list ){if(fileItem .isFormField()){// 如果是 文本域if("description1" .equals(fileItem .getFieldName())){// 如果该 FileItem 名称为 description1out.println( "遍历到 description1 ... <br/>");description1 = new String(fileItem.getString().getBytes(), "UTF-8" );}if("description2" .equals(fileItem .getFieldName())){// 如果该 FileItem 名称为 description2out.println( "遍历到 description2 ... <br/>");description2 = new String(fileItem.getString().getBytes(), "UTF-8" );}}else{// 否则,为文件域if("file1" .equals(fileItem .getFieldName())){// 客户端文件路径构建的 File 对象File remoteFile = new File(new String(fileItem .getName().getBytes(), "UTF-8"));out.println( "遍历到 file1 ... <br/>");out.println( "客户端文件位置: " + remoteFile.getAbsolutePath() + "<br/>");// 服务器端文件,放在 upload 文件夹下file1 = new File(this.getServletContext().getRealPath("attachment" ), remoteFile.getName());file1.getParentFile().mkdirs();file1.createNewFile();// 写文件,将 FileItem 的文件内容写到文件中InputStream ins = fileItem.getInputStream();OutputStream ous = new FileOutputStream(file1);try{byte[] buffer = new byte[1024];int len = 0;while((len =ins .read(buffer )) > -1)ous.write( buffer, 0, len);out.println( "已保存文件" + file1.getAbsolutePath() + "<br/>" );} finally{ous.close();ins.close();}}if("file2" .equals(fileItem .getFieldName())){// 客户端文件路径构建的 File 对象File remoteFile = new File(new String(fileItem .getName().getBytes(), "UTF-8"));out.println( "遍历到 file2 ... <br/>");out.println( "客户端文件位置: " + remoteFile.getAbsolutePath() + "<br/>");// 服务器端文件,放在 upload 文件夹下file2 = new File(this.getServletContext().getRealPath("attachment" ), remoteFile.getName());file2.getParentFile().mkdirs();file2.createNewFile();// 写文件,将 FileItem 的文件内容写到文件中InputStream ins = fileItem.getInputStream();OutputStream ous = new FileOutputStream(file2);try{byte[] buffer = new byte[1024];int len = 0;while((len =ins .read(buffer )) > -1)ous.write( buffer, 0, len);out.println( "已保存文件" + file2.getAbsolutePath() + "<br/>" );} finally{ous.close();ins.close();}}}}out.println( "Request 解析完毕" );} catch (FileUploadException e ) {// TODO Auto-generated catch blocke.printStackTrace();}out.println( "           </div>" );out.println( "      </div>" );if(file1 != null){out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>file1:</div>");out.println( "           <div align='left' class='rightDiv'>");out.println( "                <a href='" + request.getContextPath() + "/attachment/" + file1.getName() + "' target=_blank>" + file1 .getName() +  "</a>"    );out.println( "           </div>" );out.println( "      </div>" );}if(file2 != null){out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>file2:</div>");out.println( "           <div align='left' class='rightDiv'>");out.println( "                <a href='" + request.getContextPath() + "/attachment/" + URLEncoder.encode(file2.getName(), "UTF-8") + "' target=_blank>" + file2.getName() +  "</a>");out.println( "           </div>" );out.println( "      </div>" );}out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>description1:</div>");out.println( "           <div align='left' class='rightDiv'>");out.println( description1);out.println( "           </div>" );out.println( "      </div>" );out.println( "      <div class='line'>" );out.println( "           <div align='left' class='leftDiv'>description2:</div>");out.println( "           <div align='left' class='rightDiv'>");out.println( description2);out.println( "           </div>" );out.println( "      </div>" );out.println( "</fieldset></div>" );out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}}
点击上传文件,发生错误
HTTP Status 500 - The filename, directory name, or volume label syntax is incorrect
查了下:在英文操作系统下安装软件时,不要把安装包放在中文命名的文件夹下,也不要安装在以中文命名的文件夹下。
最好把安装包放在磁盘的根目录下,安装完成后再移动到其它目录中备份,不论是英文还是中文系统,安装数据库都不允许安装在以中文命名的文件夹下。
总结下来:不是路径的问题,就是文件的命名问题,更有可能是命名。
换个路径文件上传,OK。
上传文件以二进制形式提交,而非ASCII方式提交,因为Servlet不能用request.getParameter()等方式获取提交的文本内容。
3.7 带进度条的文件上传
UploadServlet只实现了普通的文件上传,如果需要显示上传进度,速度等,还需配合Ajax技术。
实时显示上传进度的原理是服务器在处理上传文件同时,将上传君度的信息写入session中,
客户端利用Ajax技术开一个线程从session中获取上传进度的信息,并实时显示。
3.7.2 上传进度条
上传进度条两个<div>标签实现,css属性显示一个HTML版的难度条
upload.jsp
<style type= "text/css">
#progressBar {width: 400px;height: 12px;background: #FFFFFF;border: 1px solid #000000;padding: 1 px
}
#progressBarItem {width: 20%;height: 100%;background: #FF0000;
}
</style>
<div id= "progressBar"><div id="progressBarItem" ></div></ div>
结果如下:
3.7.3 上传监听器
UploadListener.java
public class UploadListener implements ProgressListener {//记录上传信息的Java Beanprivate UploadStatus status;public UploadListener(UploadStatus status) {// TODO Auto-generated constructor stubthis.status = status ;}//已读取的数据长度,文件的总长度,正在保存第几个文件public void update(long bytesRead , long contentLength, int items ) {// TODO Auto-generated method stubstatus.setBytesRead( bytesRead);status.setContentLength( contentLength);status.setItems( items);}
添加了该监听器后,上传组件在上传文件时,不断的回调该方法,回传这些数据。
利用这些数据可以实时显示,因此需要保存数据,保存到UploadStatus中,普通的Java Bean
UploadStatus.java
public class UploadStatus {//已读取的数据长度,文件的总长度,正在保存第几个文件,开始上传的时间,用于计算上传速率
private long bytesRead;
private long contentLength;
private int items;
private long startTime = System.currentTimeMillis();//构造方法 set/get
public long getBytesRead() {
return bytesRead;
}public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
}public long getContentLength() {
return contentLength;
}public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}public int getItems() {
return items;
}public void setItems(int items) {
this.items = items;
}public long getStartTime() {
return startTime;
}public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
3.7.4 监听上传进度
监听上传过程需要为ServletFileUpload安装一个监听器,然后把存有上传进度信息的UploadStatus对象
放入Session,上传文件使用的Post方法;
ProgressUploadServlet.java
public class ProgressUploadServlet extends HttpServlet {/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {response.setHeader( "Cache-Control", "no-store" );response.setHeader( "Pragrma", "no-cache" );response.setDateHeader( "Expires", 0);UploadStatus status = (UploadStatus) request.getSession(true).getAttribute( "uploadStatus");if (status == null) {response.getWriter().println( "没有上传信息" );return;}long startTime = status .getStartTime();long currentTime = System.currentTimeMillis ();// 已传输的时间 单位:slong time = (currentTime - startTime ) / 1000 + 1;// 传输速度 单位:byte/sdouble velocity = ((double) status.getBytesRead()) / (double) time;// 估计总时间 单位:sdouble totalTime = status .getContentLength() / velocity;// 估计剩余时间 单位:sdouble timeLeft = totalTime - time ;// 已完成的百分比int percent = (int) (100 * (double) status.getBytesRead() / (double) status.getContentLength());// 已完成数 单位:Mdouble length = ((double) status.getBytesRead()) / 1024 / 1024;// 总长度 单位:Mdouble totalLength = ((double) status.getContentLength()) / 1024 / 1024;// 格式:百分比||已完成数(M)||文件总长度(M)||传输速率(K)||已用时间(s)||估计总时间(s)||估计剩余时间(s)||正在上传第几个文件String value = percent + "||" + length + "||" + totalLength + "||"+ velocity + "||" + time + "||" + totalTime + "||" + timeLeft+ "||" + status .getItems();response.getWriter().println( value);}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to* post.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {// 上传状态UploadStatus status = new UploadStatus();// 监听器UploadListener listener = new UploadListener(status);// 把 UploadStatus 放到 session 里request.getSession( true).setAttribute("uploadStatus" , status);// Apache 上传工具ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());// 设置 listenerupload.setProgressListener( listener);try {List itemList = upload.parseRequest(request );for (Iterator it = itemList.iterator(); it.hasNext();) {FileItem item = (FileItem) it.next();if (item .isFormField()) {System. out.println("FormField: " + item.getFieldName()+ " = " + item.getString());} else {System. out.println("File: " + item.getName());// 统一 Linux 与 windows 的路径分隔符String fileName = item.getName().replace("/" , "\\" );fileName = fileName.substring(fileName .lastIndexOf("\\"));File saved = new File("C:\\upload_test" , fileName );saved.getParentFile().mkdirs();InputStream ins = item.getInputStream();OutputStream ous = new FileOutputStream(saved);byte[] tmp = new byte[1024];int len = -1;while ((len = ins .read(tmp )) != -1) {ous.write( tmp, 0, len);}ous.close();ins.close();response.getWriter().println( "已保存文件:" + saved );}}} catch (Exception e ) {e.printStackTrace();response.getWriter().println( "上传发生错误:" + e .getMessage());}}
}
3.7.5 读取上传进度
上传进度保存在session中的uploadStatus属性中,上传文件只使用了dopost方法,没有占用doGet方法,
所以使用doget方法读取上传进度。
3.7.6 显示上传进度
上传文件时,如果不对但处理,提交表单进入另一个页面,造成页面刷新。
要使表单内容不变,同时显示进度条,直到文件上传结束,从而避免白屏,方法更改FORM的target属性
target属性默认_self,如果默认,提交后的新页面会在当前窗口显示,造成当前偶短暂白屏。
在该页面添加隐藏的iframe,将target属性指定为该iframe,则提交的新页面在iframe中显示。
表单提交了οnsubmit=“showStatus()”事件,表单提交会执行页面的showStatus方法。
Ajax能够不断刷新页面改变页面的内容,原理是创建一个request,用这个request获取其他页面的内容,并显示在页面上,因为没有在浏览器键入地址,所以不会有刷新、抖动等。
Ajax核心就是request,学名XMLHttpRequest。
progressUpload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title> Insert title here</title >
<style type= "text/css">
body, td, div {font-size: 12px; font-familly: 宋体 ; }
#progressBar {width : 400px; height: 12px; background: #FFFFFF; border : 1px solid #000000; padding: 1px; }
#progressBarItem {width : 30%; height: 100%; background: #FF0000; }
</style>
</head><body><iframe name= upload_iframe width =0 height=0></ iframe><form action= "servlet/ProgressUploadServlet" method="post" enctype="multipart/form-data" target="upload_iframe" οnsubmit="showStatus(); "><input type= "file" name ="file1" style=" width: 350px; "> <br />
<input type= "file" name ="file2" style=" width: 350px; "> <br />
<input type= "file" name ="file3" style=" width: 350px; "> <br />
<input type= "file" name ="file4" style=" width: 350px; "> <input type= "submit"value= " 开始上传 " id="btnSubmit"></ form><div id= "status" style ="display : none; ">上传进度条:<div id="progressBar">< div id="progressBarItem" ></div></ div><div id="statusInfo"></ div>
</div><br/>
<br/>
<br/>
<br/>
<br/><script type= "text/javascript">var _finished = true;function $(obj){return document.getElementById(obj);
}function showStatus(){_finished = false;$( 'status').style.display = 'block' ;$( 'progressBarItem').style.width = '1%' ;$( 'btnSubmit').disabled = true;setTimeout( "requestStatus()" , 1000);
}function requestStatus(){if(_finished) return;var req = createRequest();req.open( "GET", "servlet/ProgressUploadServlet" );req.onreadystatechange= function(){callback(req);}req.send( null);setTimeout( "requestStatus()" , 1000);
}function createRequest()
{if(window.XMLHttpRequest)//ns{return new XMLHttpRequest();} else//IE{try{return new ActiveXObject("Msxml2.XMLHTTP" );} catch(e){return new ActiveXObject("Microsoft.XMLHTTP");}}return null;
}
function callback(req){if(req.readyState == 4) {if(req.status != 200){_debug( "发生错误。 req.status: " + req.status + "" );return;}_debug( "status.jsp 返回值:" + req.responseText);var ss = req.responseText.split("||");// 格式:百分比||已完成数(M)||文件总长度(M)||传输速率(K)||已用时间(s)||估计总时间(s)||估计剩余时间(s)||正在上传第几个文件$( 'progressBarItem').style.width = '' + ss[0] + '%';$( 'statusInfo').innerHTML = '已完成百分比: ' + ss[0] + '% <br />已完成数(M): ' + ss[1] + '<br/>文件总长度(M): ' + ss[2] + '<br/>传输速率(K): ' + ss[3] + '<br/>已用时间(s): ' + ss[4] + '<br/>估计总时间(s): ' + ss[5] + '<br/>估计剩余时间(s): ' + ss[6] + '<br/>正在上传第几个文件: ' + ss[7];if(ss[1] == ss[2]){_finished = true;$( 'statusInfo').innerHTML += "<br/><br/><br/>上传已完成。" ;    $( 'btnSubmit').disabled = false;}}
}
function _debug(obj){var div = document.createElement("DIV");div.innerHTML = "[debug]: " + obj;document.body.appendChild(div);
}
</ script>
</body>
</html>
3.8 Servlet生命周期
Servlet的声明周期由web服务器来维护。
在CGI编程中,每请求一次CGI程序,服务器单独的进程处理请求,完毕后再销毁。
Servlet:服务器启动时(load-on-startup=1)或者 第一次请求Servlet(load-on-startup=0)初始化一个Servlet对象,然后利用这个对象去处理所有的客户端请求,服务器关闭才销毁这个Servlet独享。
eg
前面例子都是从doget或doPost中读取初始化参数,每次执行读取一次,效率低下,
将其放在init中,即保证了只读取一次,又保证不耽误doGet,doPost使用。
LifeCycleServlet.java
public class LifeCycleServlet extends HttpServlet {// 个税起征点 从配置文件中读取private static double startPoint = 0;/*** Initialization of the servlet. <br>** @throws ServletException*             if an error occurs*/public void init() throws ServletException {// Put your code herethis.log("执行init()方法...." );ServletConfig conf = this.getServletConfig();startPoint = Double.parseDouble(conf.getInitParameter( "startPoint"));}@Overrideprotected void service(HttpServletRequest arg0 , HttpServletResponse arg1)throws ServletException, IOException {// TODO Auto-generated method stubthis.log("执行service()方法..." );super.service(arg0 , arg1 );// 首先会执行service()方法}/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {this.log("执行 doGet() 方法 ... " );response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<link rel='stylesheet' type='text/css' href='../css/style.css'>");out.println( "<HTML><HEAD><TITLE>个人所得税计算</TITLE></HEAD>" );out.println( "<div align='center'><br/><fieldset style=width:90%><legend>个税计算器</legend><br/>");out.println( "<form method='post' action='LifeCycleServlet'>");out.println( "<div style='line'>");out.println( " <div class='leftDiv'>您的工资为</div><div align='left' class='rightDiv'><input type='text' name='income'> 单位:元</div>");out.println( "</div><br/>");out.println( "<div style='line'>");out.println( " <div class='leftDiv'></div><div align='left' class='rightDiv'><input type='submit' value='  计算个税  ' class=button></div>");out.println( "</div>");out.println( "</form>");out.println( "<BODY>");out.println( "</BODY>");out.println( "</HTML>");out.flush();out.close();}/*** The doPost method of the servlet. <br>** This method is called when a form has its tag value method equals to* post.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doPost(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {this.log("执行 doPost() 方法 ... " );response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML><HEAD><TITLE>个人所得税计算</TITLE></HEAD>" );out.println( "<link rel='stylesheet' type='text/css' href='../css/style.css'>");out.println( "<BODY>");out.println( "<div align='center'><br/><fieldset style=width:90%><legend>个税计算器</legend><br/>");try {// 从参数中获取的工资数目double income = new Double(request.getParameter("income" ));// 应纳税部分double charge = income - startPoint;// 缴税double tax = 0;if (charge <= 0) {tax = 0;}if (charge > 0 && charge <= 500) {tax = charge * 0.05;}if (charge > 500 && charge <= 2000) {tax = charge * 0.1 - 25;}if (charge > 2000 && charge <= 5000) {tax = charge * 0.15 - 125;}if (charge > 5000 && charge <= 20000) {tax = charge * 0.2 - 375;}if (charge > 20000 && charge <= 40000) {tax = charge * 0.25 - 1375;}if (charge > 40000 && charge <= 60000) {tax = charge * 0.30 - 3375;}if (charge > 60000 && charge <= 80000) {tax = charge * 0.35 - 6375;}if (charge > 80000 && charge <= 100000) {tax = charge * 0.4 - 10375;}if (charge > 100000) {tax = charge * 0.45 - 15375;}out.println( "<div style='line'>");out.println( " <div class='leftDiv'>您的工资为</div><div class='rightDiv'>"+ income + " 元</div>");out.println( "</div>");out.println( "<div style='line'>");out.println( " <div class='leftDiv'>您应纳税</div><div class='rightDiv'>"+ tax + " 元</div>");out.println( "</div><br/>");out.println( "<input type='button' οnclick='history.go(-1);' value='纳税光荣 逃税可耻 返回'  class=button>");} catch (Exception e ) {out.println( "请输入数值类型数据。<input type='button' οnclick='history.go(-1);' value='返回'  class=button>");}out.println( "</BODY>");out.println( "</HTML>");out.flush();out.close();}@Overridepublic void destroy() {// TODO Auto-generated method stubthis.log("执行 destroy() 方法 ... " );startPoint = 0;super.destroy();}

运行的结果如下:
控制台的输出:
十一月 04, 2015 9:14:06 下午 org.apache.catalina.startup.Catalina start
INFO: Server startup in 1027 ms
十一月 04, 2015 9:14:07 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行init()方法....
十一月 04, 2015 9:14:07 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行service()方法...
十一月 04, 2015 9:14:07 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行 doGet() 方法 ...
十一月 04, 2015 9:14:10 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行service()方法...
十一月 04, 2015 9:14:10 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行 doPost() 方法 ...
十一月 04, 2015 9:14:11 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行service()方法...
十一月 04, 2015 9:14:11 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行 doGet() 方法 ...
十一月 04, 2015 9:14:15 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行service()方法...
十一月 04, 2015 9:14:15 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行 doPost() 方法 ...
十一月 04, 2015 9:14:16 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行service()方法...
十一月 04, 2015 9:14:16 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行 doGet() 方法 ...
十一月 04, 2015 9:14:20 下午 org.apache.coyote.http11.Http11AprProtocol pause
INFO: Pausing Coyote HTTP/1.1 on http-8080
十一月 04, 2015 9:14:20 下午 org.apache.coyote.ajp.AjpAprProtocol pause
INFO: Pausing Coyote AJP/1.3 on ajp-8009
十一月 04, 2015 9:14:21 下午 org.apache.catalina.core.StandardService stop
INFO: Stopping service Catalina
十一月 04, 2015 9:14:21 下午 org.apache.catalina.core.ApplicationContext log
INFO: LifeCycleServlet: 执行 destroy() 方法 ...
十一月 04, 2015 9:14:21 下午 org.apache.catalina.core.ApplicationContext log
INFO: SessionListener: contextDestroyed()
十一月 04, 2015 9:14:21 下午 org.apache.catalina.core.ApplicationContext log
INFO: ContextListener: contextDestroyed()
十一月 04, 2015 9:14:21 下午 org.apache.coyote.http11.Http11AprProtocol destroy
INFO: Stopping Coyote HTTP/1.1 on http-8080
十一月 04, 2015 9:14:21 下午 org.apache.coyote.ajp.AjpAprProtocol destroy
INFO: Stopping Coyote AJP/1.3 on ajp-8009
3.8.3 注解@PostContruct @PreDestroy
两个注解用来修饰一个非静态的void方法,写在方法之前,或者void,返回类型前面
@PostContruct
public void someMethod(){...
}
public @PreDestroy void anotherMethod(){...
}
注解影响服务器启动速度,服务器启动遍历web中WEB-INF/classes下的所有class文件,以及/lib下的所有jar文件,检查哪些使用了注解,如果不用可以在web.xml中设置metadata-complete为true
3.9 Servlet之间跳转
Servlet之间可以进行跳转。
现在的MVC框架都使用了Servlet跳转,三个独立模块:Model业务处理模块(负责处理业务),View视图模块(负责显示数据),Control控制模块(负责控制)。
在Struts框架中,三部分为3个Servlet,程序在3个Servlet之间跳转。
3.9.1 转向Forward
转向通过RequestDispatcher对象的forward方法实现。
如下:
RequestDispatcher dispatcher = request.getRequestDispatcher( "/WEB-INF/web.xml");
dispatcher.forward(request , response );
getRequestDispatcher()参数必须以‘/’开始,'/'表示本web应用程序的根目录。
Forward是MVC框架中常用的技术,Forward不仅可以跳转到本应用的另一个Servlet,JSP页面,也可跳转到另外一个文件,设置WEb-INF文件夹下的文件。
使用向导,新建ForwardServlet.java
public class ForwardServlet extends HttpServlet {/*** The doGet method of the servlet. <br>** This method is called when a form has its tag value method equals to get.** @param request*            the request send by the client to the server* @param response*            the response send by the server to the client* @throws ServletException*             if an error occurred* @throws IOException*             if an error occurred*/public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {String destination = request.getParameter("destination" );// 跳转到 /WEB-INF/web.xml。通过地址栏输入网址是不能访问到该文件的,但是 forward 可以if ("file" .equals(destination )) {RequestDispatcher dispatcher = request.getRequestDispatcher( "/WEB-INF/web.xml");dispatcher.forward(request , response );}// 跳转到 /forward.jspelse if ("jsp" .equals(destination )) {// 通过 setAttribute 方法传递一个 Date 对象给 JSP 页面Date date = new Date();request.setAttribute( "date", date );RequestDispatcher dispatcher = request.getRequestDispatcher( "/forward.jsp");dispatcher.forward(request , response );}// 跳转到另一个 Servletelse if ("servlet" .equals(destination )) {RequestDispatcher dispatcher = request.getRequestDispatcher( "/servlet/LifeCycleServlet");dispatcher.forward(request , response );} else {response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html; charset=UTF-8");response.getWriter().println("缺少参数。用法:" + request.getRequestURL()+ "?destination=jsp 或者 file 或者 servlet ");}}
}

ForwardServlet根据地址栏传人的destination参数不同跳转不同的目的地,如果为file,跳转到/WEB-INF/web.xml;如果为servlet,跳转到LifeCycleServlet,如果jsp跳转到JSP页面,/forwa.jsp
forward.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<jsp:directive.page import= "java.util.Date" />
<jsp:directive.page import= "java.text.SimpleDateFormat" />
<%
Date date = (Date) request.getAttribute( "date");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML><HEAD ><TITLE> Forward 跳转</TITLE ><link rel= 'stylesheet' type ='text/css'href= '<%=request.getContextPath() %>/css/style.css'></HEAD ><BODY ><div align='center'><br /><fieldset style=width :90%><legend>Forward 跳转</legend><br /><div style=' line' ><div class= 'leftDiv' style='width :250px; '>从 ForwardServlet 中取到的 Date 为</div><div align= 'left' class='rightDiv' ><%=( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS" )).format(date) %></div></div><br /><div style=' line' ><div align= "center"><input type= 'button'οnclick= 'location="<%=request.getContextPath() %>/servlet/ForwardServlet?destination=servlet"; 'οnmοuseοver="window.status=' <%=request.getContextPath() %>/servlet/ForwardServlet?destination=servlet'"value= '跳转到 Servlet' class=button ><input type= 'button'οnclick= 'location="<%=request.getContextPath() %>/servlet/ForwardServlet?destination=file"; 'οnmοuseοver="window.status=' <%=request.getContextPath() %>/servlet/ForwardServlet?destination=file'"value= '跳转到 web.xml' class=button ><input type= 'button'οnclick= 'location="<%=request.getContextPath() %>/servlet/ForwardServlet?destination=jsp"; 'οnmοuseοver="window.status=' <%=request.getContextPath() %>/servlet/ForwardServlet?destination=jsp'"value= '跳转到 JSP' class=button ></div></div></BODY >
</HTML>
键入地址,不加参数
加参数:
forward是最常用的方式,在struts、webwork等MVC框架中,都是用Servlet处理用户请求,结果通过request.setAttribute()放到request中,然后forward到JSP中显示。
3.9.2 重定向
重定向利用服务器返回的状态码来实现,服务器通过设置HttpServletResoponse的setStatus方法设置状态码。
301 302都表示重定向,区别301永久性重定向,302临时性重定向。
下面代码将访问该Servlet的请求重定向到另一个网址
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);//设置状态码302
response.setHeader("Location",“http://www.hellodba.net”);
这个例子利用Servlet统计下载文件的下载次数。
RedirectServlet.java
public class RedirectServlet extends HttpServlet {Map<String, Integer> map = new HashMap<String, Integer>();@Overridepublic void init() throws ServletException {map.put( "/download/setup.exe", 0);map.put( "/download/application.zip", 0);map.put( "/download/01.mp3", 0);}public void doGet(HttpServletRequest request , HttpServletResponse response )throws ServletException, IOException {// response.setHeader("Refresh","1000,URL=http://localhost:8080/servlet/index.jsp");String filename = request.getParameter("filename" );if (filename != null) {int hit = map .get(filename );// 取下载次数map.put( filename, ++ hit); // 下载次数 + 1 后保存// 重定向到文件response.sendRedirect( request.getContextPath() + filename);} else {response.setCharacterEncoding( "UTF-8");response.setContentType( "text/html");PrintWriter out = response.getWriter();out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");out.println( "<HTML>");out.println( "  <HEAD><TITLE>文件下载</TITLE></HEAD>" );out.println( " <link rel='stylesheet' type='text/css' href='../css/style.css'>");out.println( "  <BODY><br/>" );out.println( "<fieldset align=center style=width:90%><legend>文件下载</legend>");out.println( "<table width=100%>");out.println( " <tr>");out.println( "      <td><b>文件名</b></td>" );out.println( "      <td><b>下载次数</b></td>" );out.println( "      <td><b>下载</b></td>" );out.println( " </tr>");for (Entry<String, Integer> entry : map.entrySet()) {out.println( "<tr>");out.println( " <td>" + entry .getKey() + "</td>");out.println( " <td>" + entry .getValue() + "</td>");out.println( " <td><a href='"+ request.getRequestURI()+ "?filename="+ entry.getKey()+ "' target='_blank' οnclick='location=location; '>下载</a></td>");out.println( "</tr>");}out.println( "</table>");out.println( " </legend>");out.println( "  </BODY>");out.println( "</HTML>");out.flush();out.close();}}@Overridepublic void destroy() {map = null;}
}

3.9.3 自动刷新
response.setHeader("Refresh","1000,URL=http://localhost:8080/servlet/index.jsp");
其中1000 时间,单位毫秒,URL指定网址。
3.10 Servlet与线程安全
由于Servlet只会有一个实例,多个用户同时请求同一个Servlet时,Tomcat会派生出多线程执行Servlet的代码,因此Servlet有线程不安全的隐患。
ThreadSafetyServlet.java
public class ThreadSafetyServlet extends HttpServlet {private String name;@Overrideprotected void doGet(HttpServletRequest req , HttpServletResponse resp)throws ServletException, IOException {name = req.getParameter( "name");resp.setCharacterEncoding( "UTF-8");resp.setContentType( "text/html");try{Thread. sleep(10000);} catch(InterruptedException e ){}resp.getWriter().println( "您好, "+ name +". 您使用了GET方式提交数据" );}//本例中不会调用dopost 方法@Overrideprotected void doPost(HttpServletRequest req , HttpServletResponse resp)throws ServletException, IOException {// TODO Auto-generated method stubname= req.getParameter( "name");resp.setCharacterEncoding( "UTF-8");resp.getWriter().println( "您好, "+name +".您使用了POST方式提交数据" );}
}
3.10.2
让doGet线程沉睡10s,但是显示的结果相同,
最后显示的结果却是相同的,原因就是后面一个执行的覆盖了前面的一个
解决的办法尽量不要使用那么属性,分别定义到方法中,
虽然使用synchronized(name)可以解决,但会造成线程等待也是不科学的。

Java Web学习笔记 3 深入Servlet技术相关推荐

  1. Java Web学习笔记10:Servlet基础

    文章目录 一.Servlet概述 1.什么是Servlet 2.Servlet架构 3.Servlet任务 4.Servlet生命周期

  2. Java Web学习笔记08:分页技术

    文章目录 一.分页技术 1.软分页 2.硬分页 (1)MySQL分页方式:通过关键字LIMIT实现

  3. 2019年Java Web学习笔记目录

    Java Web学习笔记目录 1.Java Web学习笔记01:动态网站初体验 2.Java Web学习笔记02:在Intellij里创建Web项目 3.Java Web学习笔记03:JSP元素 4. ...

  4. java web学习笔记(持续更新)

    java web学习笔记 一.Java Web简介 二.认识Servlet 1.什么是Servlet? 2.请求路径 3.tomcat 4.Servlet的使用 三.Servlet简单应用 1.创建S ...

  5. [原创]java WEB学习笔记48:其他的Servlet 监听器:域对象中属性的变更的事件监听器 (3 个),感知 Session 绑定的事件监听器(2个)...

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  6. Servlet (Java Web 学习笔记)

    Servlet 1.Servlet 技术 a)什么是 Servlet b)手动实现 Servlet 程序 c)url 地址到 Servlet 程序的访问 d)Servlet 的生命周期 e)GET 和 ...

  7. [原创]java WEB学习笔记02:javaWeb开发的目录结构

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  8. [原创]java WEB学习笔记58:Struts2学习之路---Result 详解 type属性,通配符映射

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. 1、JAVA web学习笔记

    以下内容是在学习某机构视频过程中记录的笔记,不准确的地方请大家评论指正. JavaWeb是使用Java语言开发基于互联网的项目. 资源分类有两类: 静态资源: 使用静态网页开发技术发布的资源. 特点: ...

最新文章

  1. 10.外观模式(Facade Pattern)
  2. application.properties中自定义属性的使用
  3. Python学习:字典
  4. Spring MVC如何接收浏览器传递来的请求参数--request--形参--实体类封装
  5. ActiveX插件的Z-Index属性无效问题解决
  6. Android应用程序组件Content Provider的启动过程源代码分析(5)
  7. 利用JS代码批量自动取消抖音所有关注
  8. 2020年副业推荐,介绍网络兼职的文章
  9. MinGW-w64没有bin没有进度
  10. 【Bug】Microsoft Edge拒绝访问
  11. 正常查看网页中压缩的js代码
  12. 图象处理基本算法[整理]
  13. Byval和ByRef
  14. 英国电信公布2016第一财季业绩财报 营收76亿美元
  15. H5-脱机API-Application Cache-1.1
  16. 原生js日期的格式化
  17. 为什么笔记本连接WiFi总是受限
  18. self-attention笔记
  19. python 可视化处理时间刻度
  20. 破解Navicat并登录MySQL方法

热门文章

  1. 如何使用API_api接口有什么优点
  2. 快速合并多个文件内容到一个文件中
  3. Vista HTTPS 证书错误
  4. google android win10 ios,Flutter入门安装 ,win10 Android studio 教程
  5. ligerui 表格滚动条放在表格里,固定表头
  6. 自学编程系列——2 文件路径与文本读写
  7. AWVS批量扫描-妈妈再也不用担心我不会用awvs批量扫描了
  8. Day 17 - YOLO 相关概念说明
  9. (十八)视频换-脸、无训练高速换-脸、一张图片即可完成、批量处理
  10. leetcode 121. 买卖股票的最佳时机 (贪心 + 动规 + 双指针