Servlet
用户在浏览器中输入一个网址并回车,浏览器会向服务器发送一个HTTP请求,服务器端接收并且处理改请求,然后向浏览器发送响应,浏览器接收响应,在将服务器端响应的内容显示出来,这种请求-响应模式就是典型的Web应用程序访问过程。在JavaWeb应用程序中,处理请求和响应的过程就是由Servlet程序来完成的。
1. Servlet 概述
JavaWeb应用程序中所有的请求-响应都是由Servlet来完成的。Servlet没有main方法,当用户访问服务器时,服务器通过调用Servlet的某些方法来完成整个的处理过程。
1.1 Servlet 工作流程
浏览器提交的请求是遵循HTTP协议的,提交的请求通过服务器(Tomcat)接收解析,并且封装成为HttpServletRequest
类型的reqeust对象,所有的HTTP头数据都可以通过request的相应方法查询到。
服务器(Tomcat)同时把输出流封装为HttpServletResponse
类型的response对象,通过设置response属性,就可以控制输出的内容。服务器(Tomcat)把request和response作为参数,调用Servlet的相应方法,如doGet或者doPost方法。
1.2 Servlet 接口
Servlet 实现了javax.servlet.Servlet接口的类,Servlet接口规定了特定的方法来处理特定的请求,我们只需要实现Servlet的相关方法,用户访问Web应用程序的时候,服务器(Tomcat)会自动调用这些方法来完成业务处理。Servlet业务处理主要方法有一下两种:
- Get方法:表示查询信息,URL中可以附带少量的参数信息,但是URL的总长度不能超过255个字符,并且参数会显示在浏览器的地址栏中。
- Post方法:表示提交信息,一般用于调教大数据信息或者文件,提交的内容不受长度限制,并且不会显示在浏览器地址栏。
1.3 实现 Servlet
package com.servlet.demo;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class ServletDemo implements Servlet {
private ServletConfig config;
public void destroy() {
System.out.println("执行destory()方法...");
}
public ServletConfig getServletConfig() {
return this.config;
}
public String getServletInfo() {
return this.config.getServletName();
}
public void init(ServletConfig config) throws ServletException {
System.out.println("执行init()方法...");
this.config = config;
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("执行Service()方法...");
}
}
1.4 配置 Servlet
在JavaWeb应用程序中,Web容器还必须知道浏览器怎么访问这个Servlet。所以我们要配置Servlet的类文件与访问方式。这个配置在Web应用程序的描述文件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>servlet</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!-- 配置Servlet -->
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>com.servlet.demo.ServletDemo</servlet-class>
<!-- 将servlet随Tomcat启动时自动实例化 -->
<load-on-startup>0</load-on-startup>
</servlet>
<!-- 映射Servlet -->
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
</web-app>
1.4.1 配置 servlet 标签
<servlet>
和 </servlet>
分别为Servlet配置的开始与结束标签。中间部分为Servlet的配置信息,其中 <servlet-name>
和 <servlet-class>
属性是必须配置的。
-
<servlet-name>
:配置servlet名称,可以为任意字符串,但是保证该名称在web.xml文件中唯一,该名称供<servlet-mapping>
和<filter>
等使用; -
<servlet-class>
:Servlet类所在包及类名;
1. 在容器启动时实例化 Servlet
<load-on-startup>
:配置Servlet的加载模式,可选0和1,如果配置1,服务器会在启动时自动加载该Servlet;
2. 设置Servlet初始化参数
<init-param>
:可以在servlet中配置初始化参数,包括一个参数名称和参数值,servlet可以通过其ServletConfig对象的getInitParam(String paramName)
方法来获取初始化参数值;
-
<param-name>
:配置初始化参数名称; -
<param-value>
:配置初始化参数值;
1.4.2 配置 servlet-mapping标签
-
<servlet-name>
:配置指明采用该访问方式Servlet的名称,与<servlet>
标签中的<servlet-name>
须一致; -
<url-pattern>
:配置该servlet的访问方式,该标签前面加上Web应用程序的路径,再加上服务器域名端口号信息就是访问该servlet的网址。
1.5 Servlet生命周期和生命周期方法
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 通过调用 init () 方法进行初始化;
- Servlet 调用 service() 方法来处理客户端的请求;
- Servlet 通过调用 destroy() 方法终止(结束);
以上的init()
、 service()
和 destory()
三个方法属于Servlet的生命周期方法。
- init():被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。
- service(): 执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端的请求,并把格式化的响应写回给客户端。每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。
- destory():只会被调用一次,在 Servlet 生命周期结束时被调用。
2. 通过继承 HttpServlet 编写 Servlet
直接实现Servlet接口来编写Servlet不方便,需要实现的方法太多。javax.servlet.http.HttpServlet类实现了Servlet接口所有方法,通过继承HttpServlet编写Servlet,一般只需要覆盖doGet()和doPost()方法即可。
package com.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.log("执行 doGet 方法...");
this.execute(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.log("执行 doPost 方法...");
this.execute(request, response);
}
public long getLastModified(HttpServletRequest request) {
this.log("执行 getLastModified 方法...");
return -1;
}
private void execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获取访问该 Servlet 的 URI
String requestURI = request.getRequestURI();
// 获取访问 Servlet 的方式
String method = request.getMethod();
String param = request.getParameter("param");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>First Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("以<span color='red'> " + method +"</span> 方式访问该页面,取得的 param 参数为:" + param+"。");
out.println("<br/>");
out.println("<form action='"+ requestURI +"' method='get'>");
out.println("<input type='text' name='param' value='param string'/>");
out.println("<input type='submit' value='以 GET 方式查询页面'/>");
out.println("</form>");
out.println("<form action='"+ requestURI +"' method='post'>");
out.println("<input type='text' name='param' value='param string'/>");
out.println("<input type='submit' value='以 Post 方式提交到页面'/>");
out.println("</form>");
out.println("<script type='text/javascript'>");
out.println("document.wirte('本页最后更新时间为 :' + document.lastModified);");
out.println("</script>");
out.println("</body>");
out.println("</html>");
out.flush();
out.close();
}
}
<?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>servlet</display-name>
<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>
<!-- 配置FirstServlet -->
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.servlet.FirstServlet</servlet-class>
</servlet>
<!-- 映射FirstServlet -->
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>
</web-app>
3. 请求和响应
客户端浏览器发送给一个请求,服务器接收并处理请求,最后向客户端浏览器发送响应,结束一次Web访问过程。
3.1 获取 request 的变量
客户端浏览器发出的请求被封装成为HttpServletRequest
类型对象,所有的信息包括请求地址、请求参数、提交的数据、上传的文件,客户端的IP地址,甚至是客户操作系统都包含在HttpServletRequest
类型的对象中。
package com.servlet;
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;
public class RequestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected 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();
// 获取 user-agent 请求头
String userAgent = request.getHeader("user-agent");
String os = getOS(userAgent);
String navigator = getNavigator(userAgent);
out.println("你使用的操作系统是: "+ os +" 系统");
out.println("<br/>");
out.println("你使用的浏览器是: "+ navigator +" 浏览器");
out.flush();
out.close();
}
private static String getOS(String userAgent) {
if (userAgent.indexOf("Ubuntu") > 0) {
return "Ubuntu";
}
return "未知";
}
private static String getNavigator(String userAgent) {
if (userAgent.indexOf("Firefox") > 0) {
return "Mozllia Firefox";
}
return "未知";
}
}
RequestServlet配置:
<servlet>
<servlet-name>RequestServlet</servlet-name>
<servlet-class>com.servlet.request.RequestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestServlet</servlet-name>
<url-pattern>/request</url-pattern>
</servlet-mapping>
3.2 使用 response 生成图片验证码
package com.servlet.response;
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.annotation.WebServlet;
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 {
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();
// 随机获取6位验证码
public StringBuffer getIdentityCode() {
// 创建缓存字符串
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 6; i++) {
buffer.append(CHARS[random.nextInt(CHARS.length)]);
}
return buffer;
}
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());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("image/jpeg");
String identityCode = getIdentityCode().toString();
request.getSession(true).setAttribute("identityCode", identityCode);
// 设置验证码图片的高度和宽度
int width = 100;
int height = 30;
Color backColor = getRandomColor();
Color frontColor = getReverseColor(backColor);
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(backColor);
g.fillRect(0, 0, width, height);
g.setColor(frontColor);
g.drawString(identityCode, 18, 20);
for (int i = 0; i < random.nextInt(100); 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();
}
}
IdentityServlet配置信息:
<servlet>
<servlet-name>identity</servlet-name>
<servlet-class>com.servlet.response.IdentityServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>identity</servlet-name>
<url-pattern>/identity</url-pattern>
</servlet-mapping>
4. 读取 web.xml 参数
在JavaWeb开发中,源码上微小的变动就需要重新编译class文件,然后重新部署,会带来大量的工作。现在,可以将一些常亮信息存放于配置文件中。当程序需求发生变化,只需修改配置文件即可。web.xml提供了设置初始化参数的功能,可以将这些信息配置在web.xml文件中。
4.1 初始化参数(init-param)
web.xml配置servlet时,
-
String getInitParameter(String parameterName)
: 获取指定初始化参数的值; -
Enumeration<String> getInitParameterNames()
: 获取所有初始化参数名称;
web.xml配置信息如下:
<!-- 配置InitParamServlet -->
<servlet>
<servlet-name>InitParamServlet</servlet-name>
<servlet-class>com.servlet.initParam.InitParamServlet</servlet-class>
<!-- 配置初始化参数 -->
<init-param>
<param-name>admin</param-name>
<param-value>martin0319</param-value>
</init-param>
<init-param>
<param-name>adminPassword</param-name>
<param-value>319517</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>InitParamServlet</servlet-name>
<url-pattern>/param</url-pattern>
</servlet-mapping>
InitParamServlet
package com.servlet.initParam;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class InitParamServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String requestURI = request.getRequestURI();
out.println("<!DOCTYPE HTML>");
out.println("<html>");
out.println("<head>");
out.println("<title>Test Init-Param</title>");
out.println("</head>");
out.println("<body>");
out.println("<form action='" + requestURI + "' method='post'>");
out.println("账号:<input type='text' name='loginName' />"
+ "<br />"
+ "密码:<input type='password' name='loginPassword' />"
+ "<br />"
+ "<input type='submit' value='Log In Now' />");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 获取由表达提交的数据
String username = request.getParameter("loginName");
String password = request.getParameter("loginPassword");
/*
* 获取web.xml中配置参数
* 1、获取ServletConfig对象
* 2、获取所有初始化参数名称
* 3、通过初始化参数名称获取初始化参数值
*/
ServletConfig config = this.getServletConfig();
String initName = config.getInitParameter("admin");
String initPassword = config.getInitParameter("adminPassword");
if (username.equalsIgnoreCase(initName) && password.equalsIgnoreCase(initPassword)) {
out.println("欢迎登陆...");
} else {
out.println("用户名或密码错误...");
}
}
}
4.2 上下文参数(context-param)
由于 init-param 是配置在<servlet>
标签中的,只能由这个servlet类调用,不能被其他的servlet调用,不是全局参数。如果需要配置全局参数,需要用到上下文参数(文档参数),上下文参数使用<context-param>
标签配置。
上下文参数可以使用ServletConfig
对象来获取。Servlet中通过getServletConfig().getServletContext()
方法来获取一个ServletContext
对象。
-
String getInitParameter(String parameterName)
: 获取指定上下文参数的值; -
Enumeration<String> getInitParameterNames()
: 获取所有上下文参数名称;
ContextParamServlet配置信息:
<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>.jpg, .gif, .bmp</param-value>
</context-param>
<!-- 配置 ContextParamServlet -->
<servlet>
<servlet-name>ContextParamServlet</servlet-name>
<servlet-class>com.contextParam.ContextParamServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContextParamServlet</servlet-name>
<url-pattern>/context</url-pattern>
</servlet-mapping>
ContextParamServlet类:
package com.contextParam;
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;
public class ContextParamServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 获取ServletContext对象
ServletContext context = this.getServletConfig().getServletContext();
// 获取上下文参数
String uploadFolder = context.getInitParameter("upload folder");
String allowFileType = context.getInitParameter("allowed file type");
// 获取实际磁盘路径
String realPath = context.getRealPath(uploadFolder);
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>读取上下文参数</title>");
out.println("</head>");
out.println("<body>");
out.println("<table>");
out.println("<tr>");
out.println("<td>上传文件夹:</td>");
out.println("<td>" + uploadFolder + "</td>");
out.println("</tr>");
out.println("<tr>");
out.println("<td>实际磁盘路径:</td>");
out.println("<td>" + realPath + "</td>");
out.println("</tr>");
out.println("<tr>");
out.println("<td>允许上传类型:</td>");
out.println("<td>" + allowFileType + "</td>");
out.println("</tr>");
out.println("</table>");
out.println("</body>");
out.println("</html>");
out.flush();
out.close();
}
}