Servlet技术及应用
实验预习内容
-
Web页面中都可以使用哪些方式来发送HTTP请求?
答:Web应用中可以使用GET,POST,HEAD,PUT,DELETE,CONNECT,OPTIONS,TRACE八种请求方式来发送HTTP请求。 -
GET与POST两种请求方式有什么区别?分别适用于什么情况?
答:(1)GET与POST两种请求方式的区别:
(2)GET与POST两种请求方式所适用的情况:1) 在做数据查询时,建议用GET方式;而在做数据添加、修改或删除时,建议用POST方式。2) GET方式的安全性较POST方式要差些,包含机密信息时,建议用POST方式。如果仅考虑时间因素的话,GET方式的时间效率是要优于POST方式的。 -
HTTP响应内容的类型都有哪些?如何设置HTTP响应内容的类型?
答:(1)HTTP响应内容的类型一共分为五大类:
(2)通过设置响应头来设置HTTP响应内容的类型。 -
什么是ServletContext对象,它的生存周期如何?
答:(1)ServletContext对象:ServletContext代表是一个Web应用的环境对象,内部封装了web应用的信息,一个web应用只有一个ServletContext对象,但是有多个Servlet对象。(2)ServletContext对象的生命周期:创建:服务器启动(该web应用加载到服务器中) 销毁:服务器关闭(web应用被卸载) -
ServletContext与ServletRequest的getRequestDispatcher()方法有什么区别?、
答:ServletContext对象是应用级对象(application级),而ServletRequest是request级对象。(1)如果使用绝对路径(”/index.jsp”),那他们没有区别。(2)如果使用相对路径,则只有ServletRequest.getRequestDispatcher()方法可用,因为request对象本身包含当前请求的path。
实验内容与步骤
1.简单的综合应用,掌握Servlet将请求转发给另一个Servlet的方法。
【步骤1】创建一个名为input.html的HTML页面,其中包括一个表单,表单中包含两个文本域,分别供用户输入学号和姓名,该页面也包含提交和重置按钮。
【步骤2】 定义一个名为com.demo.Student类,其中包括学号sno和姓名name两个private的成员变量,定义访问和修改sno和name的方法。
【步骤3】编写名为FirstServlet的Servlet,要求当用户在input.html中输入信息后点击“提交”按钮,请求FirstServlet对其处理。在FirstServlet中使用表单提交的请求参数(学号和姓名)创建一个Student对象并将其作为属性存储在请求作用域中,然后通过请求对象的getRequestDispatcher()方法获得RequestDispatcher()对象,将请求转发到SecondServlet。
【步骤4】在SecondServlet中取出请求作用域中存储的Student对象,并显示输出该学生的学号和姓名,5秒钟之后,自动返回input.html页面。
- input.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>输入学生信息</title>
</head>
<body>
<form action="FirstServlet" method="post">
学号<input type="text" name="sno" size="15" /><br>
姓名<input type="text" name="sname" size="15"/><br>
<input type="submit" value="登录" />
<input type="reset" value="取消" />
</form>
</body>
</html>
- Student
package com.demo;
public class Student {
private String sno;
private String name;
public Student(String sno, String name) {
// TODO Auto-generated constructor stub
this.sno=sno;
this.name=name;
}
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- FirstServlet
package com.demo;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/FirstServlet")
public class FirstServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String sno = request.getParameter("sno");
String name = request.getParameter("sname");
Student student = new Student(sno,name);
request.setAttribute("student", student);
RequestDispatcher rd = request.getRequestDispatcher("/SecondServlet");
rd.forward(request, response);
}
}
- SecondServlet
package com.demo;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/SecondServlet")
public class SecondServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Student student= (Student)request.getAttribute("student");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<title>学生信息</title>");
out.println("学号:"+student.getSno()+"<br>");
out.println("姓名:"+new String(
student.getName().getBytes("iso-8859-1"),"UTF-8")+"<br>");
//out.println("<a href='input.html'>返回输入页面</a>");
response.setHeader("Refresh","5;url=input.html");
}
}
运行结果:
2.编写一个Servlet,显示所有请求头信息:
【步骤1】在exp01项目下编写一个名为ShowHeadersServlet.java的Servlet程序,使其能够检索所有的请求头,并且能够以表格的形式将请求头的参数名和参数值显示出来。
【步骤2】在浏览器地址栏中输入正确的URL,访问并测试该Servlet。
- ShowHeadersServlet
package com.demo;
import java.io.PrintWriter;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "ShowHeadersServlet", value = "/ShowHeadersServlet")
public class ShowHeadersServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=gb2312");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>All Headers</title>");
out.println("</head>");
out.println("<body bgcolor=\"#fdf5e6\">");
out.println("<h1 align=\"center\">All Request Headers</h1>");
out.println("<table border=1 align=\"center\">\n"+"<tr bgcolor=\"#ffad00\">\n"+"<th>Header Name<th>Header Value");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = (String)headerNames.nextElement();
out.println("<tr><td>"+headerName);
out.println("<td>"+request.getHeader(headerName));
}
out.println("</table>\n</body></html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
3.编写一个名为ExcelServlet的Servlet程序,通过设置响应内容类型和输出流,向客户端浏览器响应一个带数据的Excel电子表格,程序的运行结果要求如下图所示。
- ExcelServlet
package com.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "ExcelServlet", value = "/ExcelServlet")
public class ExcelServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Cache-Control","no-cache");
response.setContentType("application/vnd.ms-excel;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<h3>Student information</h3>");
out.println("学号\t姓名\t性别\t年龄\t所在系");
out.println("95001\t李勇\t男\t20\t信息");
out.println("95002\t刘晨\t女\t19\t数学");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
4.使用ServletConfig检索Servlet的初始化参数。
【步骤1】编写ConfigDemoServlet程序,通过注释为其添加初始化参数email、telephone,在Servlet中读取该servlet的名称和初始化参数email、telephone,并将这两个参数的值在响应的HTML文本中输出。
【步骤3】在浏览器的地址栏中输入下面URL请求该Servlet:
http://localhost:8080/ exp01/configDemo.do
要求程序的运行结果如下图所示:
- ConfigDemoServlet
package com.demo;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet(name="ConfigDemoServlet",
urlPatterns = {"/configDemo.do"},
initParams = {
@WebInitParam(name = "email", value = "hacker@163.com"),
@WebInitParam(name = "telephone", value = "8899123")
})
public class ConfigDemoServlet extends HttpServlet{
String servletName = null;
ServletConfig config = null;
String email = null;
String telephone = null;
private ServletContext servletContext;
public void init(ServletConfig config) {
this.config = config;
servletName = config.getServletName();
email = config.getInitParameter("email");
telephone = config.getInitParameter("telephone");
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,IOException{
response.setContentType("text/html;charset=UTF-8");
//写入日志文件
this.servletContext=config.getServletContext();
String admin_email = servletContext.getInitParameter("admin-email");//利用名字获取值
String admin_tel = servletContext.getInitParameter("admin-tel");//利用名字获取值
servletContext.log(admin_email);
servletContext.log(admin_tel);
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<head><title>配置对象</title></head>");
out.println("Servlet名称:"+servletName+"<br>");
out.println("Email地址:"+email+"<br>");
out.println("电话:"+telephone);
out.println("</body></html>");
out.println("<br><br>");
out.println("web.xml文件中的初始化参数值admin-email: " + admin_email);
out.println("<br>");
out.println("web.xml文件中的初始化参数值admin-tel: " + admin_tel);
}
}
运行截图:
5. 为ServletContext配置初始化参数并进行访问。
在web.xml文件中为当前web应用添加两个名为admin-email和admin-tel的ServletContext初始化参数,在ConfigDemoServlet中获取这两个参数的参数值,使用ServletContext对象将admin-email和admin-tel的参数值写入日志文件中,并查看日志文件。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置整个web应用的初始化参数 -->
<context-param>
<param-name>admin-email</param-name>
<param-value>123456@qq.com</param-value>
</context-param>
<context-param>
<param-name>admin-tel</param-name>
<param-value>1234567890</param-value>
</context-param>
</web-app>
运行截图:
6. 编写一个名为CountServlet的Servlet程序:
1)使用成员变量保存该Servlet被访问的次数,试打开多个浏览器窗口模拟多个用户同时访问该Servlet,观察该Servlet被访问次数的变化。
2)修改该Servlet,通过ServletContext属性保存被多用户同时访问的次数,体会用ServletContext在应用作用域共享数据的方法。
package com.demo;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class countServlet1
*/
@WebServlet("/CountServlet")
public class CountServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public CountServlet() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置字符编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
//获取全局的共享数据
ServletContext servletContext = this.getServletContext();
//获取计数器count
Integer count = (Integer) servletContext.getAttribute("count");
//如果获取的计算器对象为空 ,说明是第一次访问,并将count,放入servletCount
if( servletContext.getAttribute("count") == null) {
count = 1;
servletContext.setAttribute("count", count);
}else {
//否则就不是第一次访问,将登陆的计数器进行加1的数据更新
servletContext.setAttribute("count", count+1);
}
//将登陆的次数显示在页面上
PrintWriter out =response.getWriter();
out.print("<!DOCTYPE html>\r\n" +
"<html>\r\n" +
"<head>\r\n" +
"<meta charset=\"UTF-8\">\r\n" +
"<title>登陆网页次数统计</title>\r\n" +
"</head>\r\n" +
"<body>");
out.print("<h1>");
out.print("count="+ servletContext.getAttribute("count"));
out.print("<h1>");
out.print("</body>\r\n" +
"</html>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
运行截图:
思考题
- 请求中的参数如果是中文,如何正常显示中文参数值?
答:对中文参数进行两次URLEncode,由于服务器会自动进行一次URLDecode,所以我们在服务端编程再进行一次URLDecode。 - 使用RequestDispatcher的forward()方法转发请求和使用HttpServletResponse的sendRedirect()方法重定向请求有什么区别?
答:(1)RequestDispatcher.forward 方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。(2)调用HttpServletResponse.sendRedirect 方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;而调用 RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。(3)HttpServletResponse.sendRedirect 方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求。(4)RequestDispatcher.forward 方法的调用者与被调用者之间共享相同的request 对象和response 对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect 方法调用者与被调用者使用各自的request 对象和response 对象,它们属于两个独立的访问请求和响应过程。(5)无论是RequestDispatcher.forward 方法,还是HttpServletResponse.sendRedirect 方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中清除。 - 什么是Web归档文件?如何为一个Web应用创建一个WAR文件?如何发布WAR文件?
答:(1)相当于 windows 下保存网页为 web档案(.mht)的功能,即把页面上所有元素(文字、格式、图片等)都保存在硬盘上的一个特殊文件里边方便离线查看和使用。(2)可以在命令行中运用jar命令。或者直接利用IDE工具进行打包。(3)方法一:直接将应用目录复制到%tomcat_home%\webapps目录下。方法二:在%tomcat_home%\conf下的server.xml的下增加<Context path=”/news”docBase=”C:\News”reloadable=”true”/>。方法三:在%tomcat_home%\conf\catalina\localhost\下创建news.xml,内容同上,文件名必须与path路径一致。方法四:在Tomcat Manager中发布:
Context Path=/news
xml configuration file URL:\conf\catalina\localhost
WAR or Directory URL:C:\News - 如何编写线程安全的Servlet?
答:(1)变量的线程安全:这里的变量指字段和共享数据(如表单参数值)。(2)属性的线程安全。(3)使用同步的集合类。(4)不要在Servlet中创建自己的线程来完成某个功能。(5)在多个servlet中对外部对象(比方文件)进行修改操作一定要加锁,做到互斥的访问。(6)javax.servlet.SingleThreadModel接口是一个标识接口,如果一个Servlet实现了这个接口,那Servlet容器将保证在一个时刻仅有一个线程可以在给定的servlet实例的service方法中执行。将其他所有请求进行排队。