概述:
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
(Servlet可以理解为是实现了Servlet接口的类)
Servlet 在 Web 应用程序中的位置:
Servlet的任务:
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
Servlet的生命周期:
- Servlet调用init()进行初始化;
- Servlet调用service()处理客户端的请求(doGet()、doPost());
- Servlet调用destroy()销毁;
- 被JVM的垃圾回收器回收;
服务器会在启动时(load-on-startup为1)或第一次请求Servlet时(load-on-startup为0)初始化一个Servlet对象,然后这个Servlet对象会去处理所有客户端请求。服务器关闭才会销毁Servlet对象。无论请求多少次Servlet,只有一个Servlet实例。多个客户端并发请求时,服务器会启动多个线程分别执行该Servlet的service()。
一个简单的Servlet实例:
Servlet类:
package com.answer.vo;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html");
PrintWriter out=response.getWriter();
out.print("<h1>666</h1>");
out.flush();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.doGet(request, response);
}
}
web.xml:
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>com.answer.vo.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
以上也可一采用注解:
@WebServlet(name = "HelloWorld",urlPatterns = "/HelloWorld")
结果:
Servlet客户端Http请求:
客户端浏览器发出的请求被封装成一个HttpServletRequest对象。所有的信息包括请求的地址,请求的参数,提交的数据,上传的文件,客户端的IP地址都包含在这个对象中。
读取Http头的方法:
可通过HttpServletRequest对象;
序号 | 方法 & 描述 |
---|---|
1 | Cookie[] getCookies() 返回一个数组,包含客户端发送该请求的所有的 Cookie 对象。 |
2 | Enumeration getAttributeNames() 返回一个枚举,包含提供给该请求可用的属性名称。 |
3 | Enumeration getHeaderNames() 返回一个枚举,包含在该请求中包含的所有的头名。 |
4 | Enumeration getParameterNames() 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。 |
5 | HttpSession getSession() 返回与该请求关联的当前 session 会话,或者如果请求没有 session 会话,则创建一个。 |
6 | HttpSession getSession(boolean create) 返回与该请求关联的当前 HttpSession,或者如果没有当前会话,且创建是真的,则返回一个新的 session 会话。 |
7 | Locale getLocale() 基于 Accept-Language 头,返回客户端接受内容的首选的区域设置。 |
8 | Object getAttribute(String name) 以对象形式返回已命名属性的值,如果没有给定名称的属性存在,则返回 null。 |
9 | ServletInputStream getInputStream() 使用 ServletInputStream,以二进制数据形式检索请求的主体。 |
10 | String getAuthType() 返回用于保护 Servlet 的身份验证方案的名称,例如,"BASIC" 或 "SSL",如果JSP没有受到保护则返回 null。 |
11 | String getCharacterEncoding() 返回请求主体中使用的字符编码的名称。 |
12 | String getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
13 | String getContextPath() 返回指示请求上下文的请求 URI 部分。 |
14 | String getHeader(String name) 以字符串形式返回指定的请求头的值。 |
15 | String getMethod() 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
16 | String getParameter(String name) 以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。 |
17 | String getPathInfo() 当请求发出时,返回与客户端发送的 URL 相关的任何额外的路径信息。 |
18 | String getProtocol() 返回请求协议的名称和版本。 |
19 | String getQueryString() 返回包含在路径后的请求 URL 中的查询字符串。 |
20 | String getRemoteAddr() 返回发送请求的客户端的互联网协议(IP)地址。 |
21 | String getRemoteHost() 返回发送请求的客户端的完全限定名称。 |
22 | String getRemoteUser() 如果用户已通过身份验证,则返回发出请求的登录用户,或者如果用户未通过身份验证,则返回 null。 |
23 | String getRequestURI() 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。 |
24 | String getRequestedSessionId() 返回由客户端指定的 session 会话 ID。 |
25 | String getServletPath() 返回调用 JSP 的请求的 URL 的一部分。 |
26 | String[] getParameterValues(String name) 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null。 |
27 | boolean isSecure() 返回一个布尔值,指示请求是否使用安全通道,如 HTTPS。 |
28 | int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。 |
29 | int getIntHeader(String name) 返回指定的请求头的值为一个 int 值。 |
30 | int getServerPort() 返回接收到这个请求的端口号。 |
请求实例:
public class RequestServlet extends HttpServlet {
private static final long serialVersionUID = -7936817351382556277L;
/**
* @param accept
* @return 客户端浏览器接受的文件类型
*/
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-excel")) buffer.append("Excel 文件, ");
if(accept.contains("application/vnd.ms-powerpoint")) buffer.append("PPT 文件, ");
if(accept.contains("application/msword")) buffer.append("Word 文件, ");
return buffer.toString().replaceAll(", $", "");
}
/**
* @param locale
* @return 语言环境名称
*/
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 "未知语言环境";
}
/**
* @param userAgent
* @return 客户端浏览器信息
*/
private String getNavigator(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 "未知浏览器";
}
/**
* @param userAgent
* @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 "未知";
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
String authType = request.getAuthType();
String localAddr = request.getLocalAddr();
Locale locale = request.getLocale();
String localName = request.getLocalName();
String contextPath = request.getContextPath();
int localPort = request.getLocalPort();
String method = request.getMethod();
String pathInfo = request.getPathInfo();
String pathTranslated = request.getPathTranslated();
String protocol = request.getProtocol();
String queryString = request.getQueryString();
String remoteAddr = request.getRemoteAddr();
int port = request.getRemotePort();
String remoteUser = request.getRemoteUser();
String requestedSessionId = request.getRequestedSessionId();
String requestURI = request.getRequestURI();
StringBuffer requestURL = request.getRequestURL();
String scheme = request.getScheme();
String serverName = request.getServerName();
int serverPort = request.getServerPort();
String servletPath = request.getServletPath();
Principal userPrincipal = request.getUserPrincipal();
String accept = request.getHeader("accept");
String referer = request.getHeader("referer");
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>");
// 这里<title></title>之间的信息在浏览器中显示为标题
out.println(" <HEAD><TITLE>Request Servlet</TITLE></HEAD>");
out.println(" <style>body, font, td, div {font-size:12px; line-height:18px; }</style>");
out.println(" <BODY>");
out.println("<b>您的IP为</b> " + remoteAddr + "<b>,位于</b> " + "xxxx" + "<b>;您使用</b> " + getOS(userAgent) + " <b>操作系统</b>," + getNavigator(userAgent) + " <b>。您使用</b> " + getLocale(locale) + "。<br/>");
out.println("<b>服务器IP为</b> " + localAddr + "<b>,位于</b> " + "xxxx" + "<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>访问到该页面。</b><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();
}
}
Servlet客户端Http响应:
服务器对客户端浏览器做出的响应被封装成一个HttpServletResponse对象,通过HttpServletResponse.getWriter()获得一个PrintWriter对象,可使用该对象输出信息。
从 Web 服务器端返回到浏览器的HTTP 1.1 响应报头;
设置Http响应报头的方法:
序号 | 方法 & 描述 |
---|---|
1 | String encodeRedirectURL(String url) 为 sendRedirect 方法中使用的指定的 URL 进行编码,或者如果编码不是必需的,则返回 URL 未改变。 |
2 | String encodeURL(String url) 对包含 session 会话 ID 的指定 URL 进行编码,或者如果编码不是必需的,则返回 URL 未改变。 |
3 | boolean containsHeader(String name) 返回一个布尔值,指示是否已经设置已命名的响应报头。 |
4 | boolean isCommitted() 返回一个布尔值,指示响应是否已经提交。 |
5 | void addCookie(Cookie cookie) 把指定的 cookie 添加到响应。 |
6 | void addDateHeader(String name, long date) 添加一个带有给定的名称和日期值的响应报头。 |
7 | void addHeader(String name, String value) 添加一个带有给定的名称和值的响应报头。 |
8 | void addIntHeader(String name, int value) 添加一个带有给定的名称和整数值的响应报头。 |
9 | void flushBuffer() 强制任何在缓冲区中的内容被写入到客户端。 |
10 | void reset() 清除缓冲区中存在的任何数据,包括状态码和头。 |
11 | void resetBuffer() 清除响应中基础缓冲区的内容,不清除状态码和头。 |
12 | void sendError(int sc) 使用指定的状态码发送错误响应到客户端,并清除缓冲区。 |
13 | void sendError(int sc, String msg) 使用指定的状态发送错误响应到客户端。 |
14 | void sendRedirect(String location) 使用指定的重定向位置 URL 发送临时重定向响应到客户端。 |
15 | void setBufferSize(int size) 为响应主体设置首选的缓冲区大小。 |
16 | void setCharacterEncoding(String charset) 设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8。 |
17 | void setContentLength(int len) 设置在 HTTP Servlet 响应中的内容主体的长度,该方法设置 HTTP Content-Length 头。 |
18 | void setContentType(String type) 如果响应还未被提交,设置被发送到客户端的响应的内容类型。 |
19 | void setDateHeader(String name, long date) 设置一个带有给定的名称和日期值的响应报头。 |
20 | void setHeader(String name, String value) 设置一个带有给定的名称和值的响应报头。 |
21 | void setIntHeader(String name, int value) 设置一个带有给定的名称和整数值的响应报头。 |
22 | void setLocale(Locale loc) 如果响应还未被提交,设置响应的区域。 |
23 | void setStatus(int sc) 为该响应设置状态码。 |
响应实例:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public class IdentityServlet extends HttpServlet {
private static final long serialVersionUID = -479885884254942306L;
public static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', '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();
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());
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("image/jpeg");
String randomString = getRandomString();
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);
}
// 转成JPEG格式
ServletOutputStream out = response.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(bi);
out.flush();
}
public static void main(String[] args) {
System.out.println(getRandomString());
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function reloadImage() {
document.getElementById("btn").disabled=true;
document.getElementById("identity").src="/IdentityServlet?ts"
+new Date().getTime();
}
</script>
</head>
<body>
<img src="/IdentityServlet" id="identity" onload="btn.disabled=false"/>
<input type="button" onclick="reloadImage()" value="换个图片" id="btn">
</body>
</html>
ServletContext对象:
ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放。WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用,并且它被所有客户端共享。由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。公共聊天室就会用到它。当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁。
其ServletContext对象具有的属性:
添加属性:setAttribute(String name, Object obj);
得到值:getAttribute(String name),这个方法返回Object
删除属性:removeAttribute(String name)
ServletContext中的属性的生命周期从创建开始,到服务器关闭结束。
实例:
public class ServletContext1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ServletContext servletContext=this.getServletContext();
servletContext.setAttribute("name","ans");
out.println("将 name=小明 写入了ServletContext");
}
}
public class ServletContext2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ServletContext servletContext=this.getServletContext();
String name=(String) servletContext.getAttribute("name");
out.println("name:"+name);
}
}
粗看之下,这个运行结果和Session,Cookie的应用似乎没什么区别,但事实上则完全不一样的。只要不关闭Tomcat或者reload该应用,当我们关闭当前的浏览器,或者是换一个浏览器,比如从360浏览器换到了IE浏览器再次访问Servlet2,我们依然可以看到这个结果!这就是和Session,Cookie最大的不同了。之所以会造成这种不同,是因为ServletContext存在于服务器内存中的一个公共空间,它可以供所有的用户客户端访问。
Servlet之间的跳转:
转向(Forward):
转向是通过RequestDispatcher对象的forward(HttpServletRequest request, HttpServletResponse response)方法实现的。RequestDispatcher可以通过getRequestDispatcher()方法来获得。其主要是把请求转发到服务器上的另一个资源。
@WebServlet(name = "ForwardServlet",urlPatterns = "/ForwardServlet")
public class ForwardServlet extends HttpServlet {
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.jsp
else 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);
}
// 跳转到另一个 Servlet
else if("servlet".equals(destination)){
RequestDispatcher dispatcher = request.getRequestDispatcher("/DatabaseAccess");
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 ");
}
}
public void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
重定向(Redirect):
重定向是利用服务器的返回的状态码来实现的。重定向请求到另一个网页的最简单的方式是使用 response 对象的 sendRedirect() 方法。
response.sendRedirect("https://www.baidu.com");
该方法把响应连同状态码和新的网页位置发送回浏览器。也可以通过把 setStatus() 和 setHeader() 方法一起使用来达到同样的效果:
String site = "http://www.baidu.com" ;
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
Servlet数据库访问(MySQL):
public class DatabaseAccess extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
String title = "数据库结果";
out.println("<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n");
try{
Class.forName("com.mysql.jdbc.Driver");
Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/bz?useUnicode=true&characterEncoding=utf-8",
"root","root");
String sql="select * from user1";
PreparedStatement ppst=connection.prepareStatement(sql);
ResultSet rst=ppst.executeQuery();
while(rst.next()){
int id = rst.getInt("id");
String name = rst.getString("name");
String pwd = rst.getString("password");
// 输出数据
out.println("ID: " + id + "<br>");
out.println("Name: " + name + "<br>");
out.println("Password: " + pwd + "<br>");
out.println("</body></html>");
}
rst.close();
ppst.close();
connection.close();
}catch (Exception e){
e.printStackTrace();
}
}
}