第2章 Java Web的核心技术----Servlet
2.1 编写第一Servlet程序----HelloWorld
可以使用MyEclipse自动创建Servlet,这样程序就直接配置好
- 1.HelloWorld.java
//省略导包
public class Helloworld extends HttpServlet {
//处理客户端get请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//设置HTTP响应头的Context-Type字段值
resp.setContentType("text/html");
//获取用于输出消息的PrintWriter对象
PrintWriter writer=resp.getWriter();
writer.println("<b>Hello world</b>");
}
}
- 2.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>ServletTest</display-name>
<servlet>
<servlet-name>Helloworld</servlet-name>
<servlet-class>com.hwp.Helloworld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Helloworld</servlet-name>
<url-pattern>/servlet/helloworld</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
- 3.项目部署运行后输入如下地址,会打印 Hello world
http://localhost:8080/ServletTest//servlet/helloworld
Servlet类必须继承
javax.servlet.http.HttpServlet
含义HTML标签,需要设置Content-Type的值,resp.setContentType("text/html")
通过获取PrintWriter对象,输出消息PrintWriter writer=resp.getWriter()
2.2 学习Servlet技术
2.2.1 配置tomcat服务器的数据库连接池
在tomcat中配置数据库连接池有如下两种方式
- 配置全局数据库连接池
- 配置局部数据库连接池
在tomcat安装目录文件\conf\server.xml
中找到<GlobalNamingResources>
标签,并加入一个子标签<Resource>
,子标签的配置如下
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
<!--配置mysql数据库的连接池,
需要做的额外步骤是将mysql的Java驱动类放到tomcat的lib目录下
-->
<Resource name="jdbc/mysqlds"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="hwp123456"
maxIdle="30"
maxWait="10000"
maxActive="100"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jsp" />
- name: 设置数据源名称,通常为“jdbc/xxx”格式
- auth: 设置数据源的管理者,其中有两个可选值Container和Application,其中Container表示由容器来创建和管理数据源, Applaction表示由Web应用来创建和管理数据源
- type: 设置数据源的类型
- maxActive 连接池可以存储的最大连接数
- maxIdle 最大空闲数
- maxWait 暂时无法获得数据库连接的等待时间
2.2.2 实例:通过数据库连接池,连接MySql数据库
2.2.3 实例:处理客户端HTTP GET请求----doGet方法
和2.1 编写第一Servlet程序----HelloWorld,示例代码一样
注意doGet方法不能通过表单post请求,如下示例代码将返回405,此处指的是单一doGet方法
2.2.4 实例:处理客户端HTTP POST请求----doPost方法
- 1.使用MyEclipse自动创建名为estDoPost的Servlet类
public class TestDoPost extends HttpServlet {
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();
}
}
- 2.配置web.xml(自动生成的servlet,会自动配置web.xml)
<servlet>
<servlet-name>TestDoPost</servlet-name>
<servlet-class>com.hwp.TestDoPost</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestDoPost</servlet-name>
<url-pattern>/servlet/TestDoPost</url-pattern>
</servlet-mapping>
- 3.创建DoPost.jsp,用于处理HTTP POST方法
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'DoPost.jsp' starting page</title>
</head>
<body>
<form action="./servlet/TestDoPost" method="post">
<input type="text" name="name"/><p/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
项目部署运行,浏览器访问后http://localhost:8080/ServletTest/DoPost.jsp
点击会输出
This is class com.hwp.TestDoPost, using the POST method
注意doPost方法浏览器直接访问
http://localhost:8080/ServletTest/servlet/TestDoPost
会报405,此处指的是单一doPost方法
2.2.5 实例:处理客户端各种请求----service方法
同时处理HTTP GET和HTTP POST请求
- 1.方法一(web.xml省略)
public class TestGetAndPost extends HttpServlet{
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//编写实际方法
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//转发调用
doGet(request,response);
}
}
- 2.方法二(web.xml省略)
public class TestGetAndPost extends HttpServlet{
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// 存在super.service(arg0, arg1),doPost、doGet仍然还可以调用
//super.service(arg0, arg1);
}
}
2.2.6 初始化(init)和销毁(destory)servlet
public class Helloworld extends HttpServlet {
//处理客户端get请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
//该方法将在web服务器第一次实例化时被调用
@Override
public void init() throws ServletException {
}
//将在web服务器销毁实例时调用
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
2.2.7 实例:输出字符流响应消息----PrintWriter类
- setContentType方法需要在getWriter方法之前调用
resp.setContentType("text/html");
//获取用于输出消息的PrintWriter对象
PrintWriter writer=resp.getWriter();
- setHeader方法无论在 getWriter前后都可以设置
示例代码
public class TestPrintWriter extends HttpServlet {
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
arg1.setCharacterEncoding("utf-8");
arg1.setContentType("text/html;charset=utf-8");
arg1.setHeader("myhead1", "value1");
PrintWriter writer=arg1.getWriter();
arg1.setHeader("Content-Type","text/html;charset=utf-8");
writer.println("<b>响应消息</b>");
}
}
2.2.8 实例:输出字节流响应消息----ServletOutputStream类
演示如何使用 ServletOutputStream对象在客户端浏览器显示图像
//web.xml配置省略
public class ShowImage extends HttpServlet {
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
//设置响应消息类型为图像
arg1.setContentType("image/jpeg");
//获取ServletOutputStream对象
OutputStream outputStream=arg1.getOutputStream();
byte[] buffer = new byte[8192];//每次从文件输入流中读取8k字节
//获取name请求参数所指定的图像绝对路径
String imgName = arg0.getParameter("name");
//获取图像文件的输入流
FileInputStream fis = new FileInputStream(imgName);
int count=0;
//通过循环读取并传送name所指定的图像数据
while(true){
//将字节读到buffer缓冲区
count=fis.read(buffer);
//当文件输入流中的字节读完后,退出while循环
if(count<=0)
break;
//向客户端输出图像字节消息
outputStream.write(buffer, 0, count);
}
//关闭文件输入流
fis.close();
}
}
浏览器输入地址,
http://localhost:8080/ServletTest/servlet/ShowImage?name=d:\2.jpg
,会显示图像
2.2.9 实例:包含web资源----RequestDispather.include方法
实现代码复用,RequestDispatcher.include方法可以include,servlet和静态的html
- 1.IncludingServlet.java
public class IncludingServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
arg1.setContentType("text/html;charset=gb2312");
PrintWriter p = arg1.getWriter();
p.println("中国</br>");
p.println(""+arg0.getRequestURI()+"<p/>");
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/servlet/IncludedServlet");
//包含IncludedServlet
rd.include(arg0, arg1);
rd = this.getServletContext().getRequestDispatcher("/IncludedHtml.html");
//包含IncludedHtml.heml页面
rd.include(arg0, arg1);
}
}
- 2.IncludedServlet.java
public class IncludedServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
arg1.setContentType("text/plain;charset=utf-8");
PrintWriter printWriter = arg1.getWriter();
printWriter.println("</b>超人</b></br>");
printWriter.println(arg0.getRequestURI()+"<p/>");
}
}
- 3.IncludedHtml.html
<!DOCTYPE html>
<html>
<head>
<title>IncludedHtml.html</title>
<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<table border="1">
<tr>
<td> 书名 </td>
<td> 出版日期 </td>
</tr>
<tr>
<td> AAA </td>
<td> 2019-10-28 10:53:32 </td>
</tr>
</table>
</body>
</html>
- 部署运行,浏览器输入如下地址
2.2.10 实例:转发web资源----RequestDispather.forward方法
- forward方法只能使用一次,否则会抛出IllegalStateException异常,而include方法可以多次使用
- 在调用forward方法之前,输出缓冲区中的数据会被清空,也就是说,使用forward方法进行请求转发时,只能输出被转发的Web资源中的消息
- 在调用者和被调用者中设置响应消息头都不会被忽略,而调用include方法时,只有在调用者中设置响应消息头才会生效
public class ForwardServlet extends HttpServlet{
@Override
protected void service (HttpServletRequest arg0,HttpServletResponse arg1) throws ServletException,IOException{
RequestDispatcher rd=getServletContext.getRequestDispatcher("/servlet/IncludingServlet");
rd.forward(arg0,arg1);
}
}
2.3 掌握HttpServletResponse类
2.3.1 产生状态响应码
2.3.2 设置响应消息头
以下内容就是一组标准的响应消息头,【】为注解
Location: http://www.baidu.org/index.jsp 【让浏览器重新定位到url】
Server:apache tomcat 【告诉浏览器我是tomcat】
Content-Encoding: gzip 【告诉浏览器我使用 gzip】
Content-Length: 80 【告诉浏览器会送的数据大小80节】
Content-Language: zh-cn 【支持中文】
Content-Type: text/html; charset=GB2312 [内容格式text/html; 编码gab2312]
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT 【告诉浏览器,该资源上次更新时间】
Refresh: 1;url=http://www.baidu.com 【过多久去,刷新到 http://www.baidu.com】
Content-Disposition: attachment; filename=aaa.zip 【告诉浏览器,有文件下载】
Transfer-Encoding: chunked [传输的编码]
Set-Cookie:SS=Q0=5Lb_nQ; path=/search[后面详讲]
Expires: -1[告诉浏览器如何缓存页面IE]
Cache-Control: no-cache [告诉浏览器如何缓存页面火狐]
Pragma: no-cache [告诉浏览器如何缓存页面]
Connection: close/Keep-Alive [保持连接 1.1是Keep-Alive]
Date: Tue, 11 Jul 2000 18:23:51 GMT
- 1.添加和设置响应消息头
public void addHeader(String name,String value);
public void setHeader(String name,String value);
- 2.操作整数型的响应消息头
public void addIntHeader(String name,String value);
public void setIntHeader(String name,String value);
- 3.操作时间类型的响应消息头
public void addDateHeader(String name,long date);
public void setDateHeader(String name,long date);
- 4.设置响应正文类型的响应消息头
response.setContentType(“image/jpeg”);
- 5.设置响应正文字符集的响应消息头
response.setContentType(“text/html;charset=utf-8”);
- 6.设置响应正文字大小的响应消息头
- 7.检查响应消息头
public boolean containsHeader(String name);
2.3.3 实例:验证响应信息头设置情况
public class ResponseHeaderDemo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;Charset=UTF-8");
response.setHeader("Content-Length", "1234");
response.addHeader("NewField1", "value1");
Calendar calendar=Calendar.getInstance();
calendar.setTime(new Date());
response.addDateHeader("MyDate", calendar.getTimeInMillis());
response.setIntHeader("NewField2", 3000);
}
}
http://localhost:8080/ServletTest//servlet/HeaderDemo
运行后,消息头
Content-Length: 1234
Content-Type: text/html;charset=UTF-8
Date: Mon, 28 Oct 2019 06:40:46 GMT
MyDate: Mon, 28 Oct 2019 06:40:46 GMT
NewField1: value1
NewField2: 3000
2.4 掌握HttpServletRequest类
2.4.1 获取请求行消息
获取请求行消息的方法,使用>http://localhost:8080/ServletTest/servlet/HeaderDemo?name=bill&age=52
连接说明解释
方法名 | 功能 | 值 |
---|---|---|
getMethod() | 返回请求行中的请求方法(如GET、POST、PUT等) | GET |
getRequestURI()返回请求行中的资源部分(不包括参数部分) | /ServletTest/servlet/HeaderDemo |
|
getQueryString() | 返回请求行中的参数部分,也就是资源路径后面问号以后的所有内容 | name=bill&age=52 |
getProtocol() | 返回请求行中的协议部分 | HTTP/1.1 |
getContextPath() | 返回web应用程序的上下文路径,实际上就是<Context>标签中的path属性值 | /ServletTest |
getPathInfo() | 返回额外路径部分,没有额外路径,返回null | null |
getPathTranslated() | 获得额外路径在服务端的本地路径,没有额外路径,返回null | null |
getServletPath() | 返回web.xml 中<url-pattern> 标签定义的Servlet映射路径 |
/servlet/HeaderDemo |
getParameter(arg0) | 返回某一个参数的值,如获得name参数的值,代码是getParameter(name)
|
bill |
2.4.2 获取网络连接信息
假设客户端IP是192.168.18.10
,服务端的IP是192.168.18.254
,服务器主机名为webserver,通过使用>http://localhost:8080/ServletTest/servlet/HeaderDemo?name=bill&age=52
来访问servlet
产生的HTTP请求消息
GET /ServletTest/servlet/HeaderDemo?name=bill&age=52 HTTP/1.1
Accept:/
Accept-Language: zh-cn
Accept-Encoding: gzip,deflate
User-Agent:Mozilla/4.0
Host:localhost:8080
Connection:Keep-Alive
获取网络消息的方法
方法名 | 功能 | 返回值 |
---|---|---|
getRemoteAddr() | 返回客户机用于发送请求的IP地址 | 192.168.18.10 |
getRemoteHost() | 返回客户机主机名 | 192.168.18.10 |
getRemotePort() | 返回客户机端口 | 1056(这里是列举,要看实际端口) |
getLocalAddr() | 返回web服务器接收请求网络的ip地址 | 192.168.18.254 |
getLocalName() | 返回web服务器接收请求网络的ip地址所对应的主机名 | webserver |
getLocalPort() | 返回web服务器接收请求网络接口的端口 | 8080 |
getScheme() | 返回请求协议,如http、https | http |
getServerPort() | 返回HTTP请求消息的Host字段的值的端口号 | 8080 |
getServerName() | 返回HTTP请求消息的Host字段的值的主机名 | localhost |
getRequestURL() | 返回完整的请求URL(不包括参数部分) | http://localhost:8080/ServletTest/servlet/HeaderDemo |
2.4.3 获取请求头消息
方法名 | 功能 | 返回值 |
---|---|---|
getHeader(arg0) | 返回指定的HTTP请求消息头字段的值,如getHeader("Host") | localhost:8080 |
getHeaderNames() | 略 | 所有的字段名 |
getHeaders(arg0) | 略 | 由所有重复字段的值组成 |
getIntHeader(arg0) | 返回一个指定的头字段,并将其转换为整型 | 整数 |
getDateHeader(arg0) | 返回一个指定的头字段,并将其按GMT时间转换为长整型 | 长整型 |
getContentType() | 返回请求消息中请求正文的内容类型 | null |
getContentLength() | 返回请求消息中请求正文的长度(单位,字节),如果未指定长度,返回-1 | -1 |
getCharacterEncoding() | 返回请求正文的字符编码集,没有指定编码集返回null | null |
2.5 处理cookie
2.5.1 什么是cookie
略
2.5.2 认识操作cookie发方法
略
2.5.3 实例:通过cookie技术读写客户端信息
创建SaveCookie.java
public class SaveCookie extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置Content-Type
response.setContentType("text/html;chartset=UTF-8");
//设置临时cookie,临时cookie不用设置MaxAge属性
Cookie tempCookie=new Cookie("temp", "123456");
//添加临时cookie
response.addCookie(tempCookie);
//设置MaxAge为0的cookie
Cookie cookie=new Cookie("cookie","66");
//MaxAge设置为0,浏览器接收到Cookie后,Cookie立即被删除
cookie.setMaxAge(0);
//添加超时时间为0的cookie对象
response.addCookie(cookie);
//获取请求参数user的值,即?uesr=后面的值
String user=request.getParameter("user");
//如果请求url含有user参数,创建这个永久的cookie
if(user!=null){
//创建永久cookie对象
Cookie userCookie=new Cookie("user",user);
//将MaxAge设为1天
userCookie.setMaxAge(60*60*24);
//这个cookie对站点中所有目录下的访问路径都有效
userCookie.setPath("/");
//添加永久cookie
response.addCookie(userCookie);
}
//转发到ReadCookie,并读出已经保存的cookie
RequestDispatcher readCookie = getServletContext().getRequestDispatcher("/servlet/ReadCookie");
readCookie.include(request, response);
}
}
创建ReadCookie.java,这个类负责读取被保存的Cookie值
public class ReadCookie extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
Cookie tempCookie=getCokieValue(request.getCookies(),"temp");
Cookie cookie = null;
if(tempCookie!=null)
writer.println("临时Cookie值"+tempCookie.getValue()+"<br/>");
else
cookie=getCokieValue(request.getCookies(),"cookie");
if(cookie!=null)
writer.println("Cookie值"+cookie.getValue()+"<br/>");
else
writer.println("Cookie值已被删除<br/>");
Cookie userCookie = getCokieValue(request.getCookies(),"user");
if(userCookie!=null)
writer.println("userCookie值"+userCookie.getValue()+"<br/>");
else
writer.println("userCookie未设置<br/>");
}
private Cookie getCokieValue(Cookie[] cookies, String name) {
if(cookies!=null){
for(Cookie c:cookies){
if(c.getName().equals(name)){
return c;
}
}
}
return null;
}
}
2.5.4 实例:通过cookie技术读写复杂数据
略
2.6 处理session
2.6.1 什么是session
Session是一种将信息保存在服务端的技术,可以通过HttpServletRequest的getSession()方法获取Session对象
2.6.2 认识操作seesion的方法
- getId() 返回当前HttpSession对象的ID
- getCreationTime() 返回当前HttpSession对象创建的时间,毫秒值
- setMaxInactiveInterval(arg0) 和getMaxInactiveInterval() 设置和返回当前HttpSession对象可空闲的最长时间(毫秒值)
- isNew() 判断当前的HttpSession对象是否是新创建的
- invalidate() 强制当前HttpSession对象失效,用于释放对象
- getServletContext() 获取上下文
- setAttribute(arg0, arg1) 将一个String类型的ID和一个对象关联,并将其保存在当前的HttpSession对象中
- getAttribute(arg0) 返回一个和String类型ID相关联的对象
- removeAttribute(arg0) 用于删除一个String类型的ID相关联的对象
- getAttributeNames() 用于返回一个包含当前HttpSession对象中所有属性名的Enumeration对象
2.6.3 创建session对象
public HttpSession getSession();
//如果参数为true,与无参数重载完全一样,如果参数为false,当请求消息中不包含SessionID时,并不创建一个新的HttpSession对象,而是直接返回null
public HttpSession getSession(boolean create);
2.6.4 实例:通过Cookie跟着Session
public class SessionServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60*60*24);
if(session.isNew()){
session.setAttribute("session","宇宙");
writer.println("会话已建立");
}else{
writer.println("会话属性值"+session.getAttribute("session"));
}
}
}
当第一次和第二次访问地址时,打印的内容不一样
2.6.5 实例:通过重写URL跟踪session
略
2.7 解决web开发乱码问题
2.7.1 认识java语言编码原理
略
2.7.2 实例:解决输出乱码问题
常用的方法
public class SessionServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String s="中文消息";
//设置编码格式
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer=response.getWriter();
writer.println(s);
}
}
有的web服务器,常用的方法依然失效,另一种方法
public class SessionServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String s="中文消息";
PrintWriter writer=response.getWriter();
//设置客户端的解码方式为utf-8
response.setHeader("Content-Type","text/html;charset=utf-8");
//获得utf-8编码的字节数组后,将其原样保存在String对象中
writer.println(new String(s.getBytes("utf-8"),"iso-8859-1"));
}
}
2.7.3 实例:解决服务端程序读取中文请求消息的乱码问题
略
2.7.4 实例:用Ajax技术发送和接收中文信息
略
2.7.5 实例:实现请求消息头和响应消息头中传输中文
略