【JavaWeb学习】07:Servlet高级

第七章:Servlet高级


在这里插入图片描述

Filter与Listener是Servlet规范中的两个高级特性,不同于Servlet其不用处理客户端请求。

Filter用于对request、response对象进行修改,Listener用于对context、session、request事件进行监听,善用两个对象。

一、Filter过滤器

1.Filter概念

Filter被称为过滤器,其基本功能是对Servelet容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理前后实现一些特殊功能。

Filter过滤器实现了javax.servlet.Filter接口的类,在javax.servlet.Filter接口中定义了3个方法,如下表:

方法声明说明
init(FilterConfig filterConfig)初始化过滤器
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)参数chain代表当前Filter链的对象
destroy()该方法在web服务器卸载Filter对象之前被调用,用于释放Filter对象打开的资源

注意:如果要用到FilterConfig对象,那么初始化代码就只能在Filter的init()方法中编写,而不能在构造方法中

以上3个方法都是Filter的生命周期方法,init()在web应用程序加载的时候被调用、destroy()在web应用程序卸载时调用:

doFilter()方法只要有客户端请求时就会被调用,并且Filter所有的工作集中在该方法之中。

注意:init()与destroy()方法都只会被调用一次

2.Filter映射

Filter拦截的资源需要在web.xml文件中进行配置,这些配置信息就是Filter映射。

过滤器的配置信息中包含多个元素,这些元素分别具有不同的作用具体如下:

配置信息中元素说明
<filter>根元素,用于注册一个Filter
<filter-name>子元素用于设置Filter名称
<filter-class>子元素用于设置Filter类的完整名称
<filter-mapping>根元素用于设置一个过滤器所拦截的资源
<url-pattern>子元素用于匹配用户请求的URL

🚩具体使用如下:

在这里插入图片描述

package c8p1;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("Hello MyServlet");
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package c8p1;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		//1.过滤器对象在初始化时调用,可以配置一些初始化参数
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//2.用于拦截用户的请求,如果和当前过滤器的拦截路径匹配,该方法就会被调用
		PrintWriter out = response.getWriter();
		out.write("Hello MyFilter");
	}
	public void destroy() {
		//3.过滤器对象在销毁时自动调用,释放资源
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<servlet>
		<servlet-name>MyServlet</servlet-name>
		<servlet-class>c8p1.MyServlet</servlet-class>	
	</servlet>
	<servlet-mapping>
		<servlet-name>MyServlet</servlet-name>
		<url-pattern>/MyServlet</url-pattern>
	</servlet-mapping>
	<filter>
		<filter-name>MyFilter</filter-name>
		<filter-class>c8p1.MyFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>MyFilter</filter-name>
		<url-pattern>/MyFilter</url-pattern>
	</filter-mapping>
</web-app>

注意:上述请求中设置了设置了对/MyServlet请求资源进行拦截,将在请求到达MyServlet程序前执行MyFilter程序。

在这里插入图片描述

分析:通过url地址直接访问MyServlet资源没有显示Hello MyServlet,而显示为Hello MyFilter(由于Filter拦截)

(1)拦截全部请求:通配符*

Filter的<filter-mapping>元素用于配置过滤器拦截的资源信息,使用通配符*来拦截所有的请求访问:

<filter>
	<filter-name>MyFilter</filter-name>
	<filter-class>c8p1.MyFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>MyFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
(2)拦截不同方式的访问请求

Filter的<filter-mapping>元素用于配置过滤器拦截的资源信息,

其子元素<dispatcher>用于指定过滤器所拦截的资源被Servlet容器调用的方式,其值共有4个:

case1:REQUEST

当用户直接访问页面时,web容器将会调用过滤器。

若目标资源是通过RequestDispatcher的include()forward()方法访问的,那么过滤器将不会被调用。

case2:INCLUDE

如果目标资源是通过RequestDispatcher的include()方法访问的,那么过滤器将被调用。

case3:FORWARD

如果目标资源是通过RequestDispatcher的forward()方法访问的,那么过滤器将被调用。

case4:ERROR

如果目标资源是通过声明式异常处理机制调用的,那么过滤器将被调用。

🚩具体使用如下:

在这里插入图片描述

package c8p2;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// ForwardServlet用于将请求转发给first.jsp页面
public class ForwardServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.getRequestDispatcher("/first.jsp").forward(request, response);
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package c8p2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

// 该过滤器用于对first.jsp页面的访问进行拦截
public class ForwardFilter implements Filter{
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// 1.过滤器对象在初始化时调用,可以配置一些初始化参数
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 2.用于拦截用户的请求,如果和当前过滤器的拦截路径匹配,该方法会被调用
		PrintWriter out = response.getWriter();
		out.write("Hello FilterTest_FORWARD");
	}
	@Override
	public void destroy() {
		// 3.过滤器对象在销毁时自动调用
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<servlet>
		<servlet-name>ForwardServlet</servlet-name>
		<servlet-class>c8p2.ForwardServlet</servlet-class>	
	</servlet>
	<servlet-mapping>
		<servlet-name>ForwardServlet</servlet-name>
		<url-pattern>/ForwardServlet</url-pattern>
	</servlet-mapping>
	<filter>
		<filter-name>ForwardFilter</filter-name>
		<filter-class>c8p2.ForwardFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>ForwardFilter</filter-name>
		<url-pattern>/first.jsp</url-pattern>
        <dispatcher>FORWARD</dispatcher>
	</filter-mapping>
</web-app>

在这里插入图片描述

分析:通过在web.xml文件中设置了<dispatcher>FORWARD</dispatcher>后:

当目标资源通过RequestDispatcher的forward()方法访问时,Filter过滤器将被调用。(first.jsp的页面内容并没有显示,显示内容为ForwardFilter类中的内容)

3.Filter链

在一个Web应用程序中可以注册多个Filter程序,每个Filter程序都可以针对某个URL进行拦截。

如果多个Filter程序对一个URL进行拦截,那么这些Filter就会组成一个Filter链(过滤器链)。

Filter链用FilterChain对象表示,FilterChain对象有一个doFilter()方法,让Filter链上当前过滤器放行(使请求进入下一个Filter)。

在这里插入图片描述

🚩具体使用如下:

在这里插入图片描述

package c8p3;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter01 implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		//1.过滤器对象在初始化时调用,可以配置一些初始化参数
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//2.用于拦截用户的请求,如果和当前过滤器的拦截路径匹配,该方法就会被调用
		PrintWriter out = response.getWriter();
		out.write("Hello MyFilter01<br />");
		chain.doFilter(request, response);
	}
	public void destroy() {
		//3.过滤器对象在销毁时自动调用,释放资源
	}
}
package c8p3;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter02 implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		//1.过滤器对象在初始化时调用,可以配置一些初始化参数
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//2.用于拦截用户的请求,如果和当前过滤器的拦截路径匹配,该方法就会被调用
		PrintWriter out = response.getWriter();
		out.write("Hello MyFilter02 Before<br />");
		chain.doFilter(request, response);
		out.write("<br />Hello MyFilter02 After<br />");
	}
	public void destroy() {
		//3.过滤器对象在销毁时自动调用,释放资源
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<servlet>
		<servlet-name>MyServlet</servlet-name>
		<servlet-class>c8p1.MyServlet</servlet-class>	
	</servlet>
	<servlet-mapping>
		<servlet-name>MyServlet</servlet-name>
		<url-pattern>/MyServlet</url-pattern>
	</servlet-mapping>
	<filter>
		<filter-name>MyFilter01</filter-name>
		<filter-class>c8p3.MyFilter01</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>MyFilter01</filter-name>
		<url-pattern>/MyServlet</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>MyFilter02</filter-name>
		<filter-class>c8p2.MyFilter02</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>MyFilter02</filter-name>
		<url-pattern>/MyServlet</url-pattern>
	</filter-mapping>
</web-app>

注意:Filter链中各个Filter的拦截顺序与它们在web.xml文件中<filter-mapping>元素的映射顺序是一致的

在这里插入图片描述

在这里插入图片描述

分析:在对MyServlet资源进行访问时,首先被MyFilter01拦截(输出其中内容),后被MyFilter02拦截,被放行之后才输出MyServlet中的内容。

✔ 存在的疑问

⚠️问题1:上述程序中发现输出的<br />标签使用chrome浏览器时不被解析,而使用IE浏览器时才能被正常解析

问题解答

⚠️问题2:为什么MyFilter02 After会被输出显示?

在MyFilter02中,当调用到doFilter()方法时,请求将直接被发送给目标资源。语句out.write("<br /> Hello MyFilter02 After <br />");将不被执行?

在这里插入图片描述

4.FilterConfig接口

为了获取Filter程序在web.xml文件中的配置信息,Servlet API提供了一个FIiterConfig接口,

该接口封装了Filter程序在web.xml中的所有配置信息,并提供了一系列获取这些配置信息的方法:

方法名说明
String getFilterName()返回在web.xml文件中为Filter设置的名称<filter-name>
String getInitParameter(String name)返回在web.xml文件中为Filter设置的某个名称的初始化参数值
Enumeration getInitParameterNames()返回在web.xml文件中为Filter设置的所有初始化参数的名称
ServletContext getServletContext()返回FilterConfig对象中所包装的ServletContext对象的引用

🚩具体使用如下:

package c8p1;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("Hello MyServlet");
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package c8p4;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter03 implements Filter {
	private String characterEncoding;
	FilterConfig fc;
	public void init(FilterConfig filterConfig) throws ServletException {
		// 1.获取FilterConfig对象
		this.fc = filterConfig;
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 2.输出参数信息
		characterEncoding = fc.getInitParameter("encoding");
		System.out.println("encoding初始化参数的值为:" + characterEncoding);
		chain.doFilter(request, response);
	}
	public void destroy() {
		//3.过滤器对象在销毁时自动调用,释放资源
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<!-- c8p4 -->
	<filter>
		<filter-name>MyFilter03</filter-name>
		<filter-class>c8p4.MyFilter03</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>GBK</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>MyFilter03</filter-name>
		<url-pattern>/MyServlet</url-pattern>
	</filter-mapping>
</web-app>

在这里插入图片描述
在这里插入图片描述

分析:浏览器窗口中输出了Hello MyServlet,而在控制台窗口输出了encoding的参数信息

5.应用1:Filter实现用户自动登录(存在问题)
(1)Cookie自动登录:

Cookie可以实现用户自动登录功能:

当用户第一次访问服务器时,服务器会发送一个包含用户信息的Cookie,

在这里插入图片描述

当客户端再次访问服务器时,会向服务器回送Cookie,服务器从Cookie中获取用户信息,从而实现用户的自动登录功能。

在这里插入图片描述

注意:当客户端访问服务器的Servlet时,所有Servlet都需要对用户的Cookie信息进行校验(造成代码重复)

可以在Filter程序中实现Cookie的检验,简化代码:

由于Filter可以对服务器的所有请求进行拦截,因此一旦通过Filter程序就相当于用户信息通过了校验(Servlet程序根据获取到的用户信息就可以实现自动登录)

(2)Filter简化Cooie自动登录:

在这里插入图片描述
在这里插入图片描述

step1:User类
package c8p5;

public class User {
	private String username;
	private String password;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}
step2:jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
	<head>
		<title>Insert title here</title>
	</head>
	<body style="text-align: center;">
		<h3>用户登录</h3>
		<form action="${pageContext.request.contextPath}/LoginServlet" method="post">
			<table border="1" width="800px" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
				<tr>
					<td height="30">用户名:</td>
					<td>&nbsp;&nbsp;<input type="text" name="username" />${errerMsg}</td>
				</tr>
				<tr>
					<td height="30">&nbsp;码:</td>
					<td>&nbsp;&nbsp;<input type="password" name="password" /></td>
				</tr>
				<tr>
					<td height="35">自动登录时间</td>
					<td>
						<input type="radio" name="autologin" value="&{60*60*24*31}" />一个月
						<input type="radio" name="autologin" value="&{60*60*24*31*3}" />三个月
						<input type="radio" name="autologin" value="&{60*60*24*31*6}" />半年
						<input type="radio" name="autologin" value="&{60*60*24*31*12}" />一年
					</td>
				</tr>
				<tr>
					<td height="30" colspan="2">
						<input type="submit" value="登录" />&nbsp;&nbsp;&nbsp;&nbsp;
						<input type="reset" value="重置" />
					</td>
				</tr>
			</table>
		</form>
	</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="utf-8" import="java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
	<head>
		<title>显示登录的用户信息</title>
	</head>
	<body>
		<c:choose>
			<c:when test="${sessionScope.user == null}">
				<!-- 1.没有登录过将显示用户登录超链接 -->
				<br />
				<center><h3>Please login first</h3></center>
				<a href="${pageContext.request.contextPath}/login.jsp">点击登录</a>
			</c:when>
			<c:otherwise>
				<!-- 2.已经登录过将显示登录的用户名以及注销按钮 -->
				<br />
				<center><h3>Welcome to the site!</h3></center>
				欢迎你,${sessionScope.user.username}!
				<a href="${pageContext.request.contextPath}/LogoutServlet">点击注销</a>
			</c:otherwise>
		</c:choose>
		<hr />
	</body>
</html>
step3:Servlet(Login&Logout)
package c8p5;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 1.获取用户名和密码
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		// 2.检查用户名和密码
		if ("lch".equals(username) && "123456".equals(password)) {
			// 登录成功
			// (1)将用户状态user对象存入session域中
			User user = new User();
			user.setUsername(username);
			user.setPassword(password);
			request.getSession().setAttribute("user", user);
			// (2)获取用户是否登录的标识符autologin,if判断是否发送自动登录的Cookie(autoLogin)
			String autologin = request.getParameter("autologin");
			if (autologin != null) {
				// 用户点击了自动登录->将用户信息保存到cookie中(将在AutoLoginFilter中被使用)
				Cookie cookie = new Cookie("autoLogin", username + "-" + password);
                int time = Integer.parseInt(autologin);
				cookie.setMaxAge(time);
				cookie.setPath(request.getContextPath());
				response.addCookie(cookie);
			}
            // (3)重定向index主页面,回显用户信息
			response.sendRedirect(request.getContextPath() + "/index.jsp");
		} else {
			// 登录失败
            // (1)保存登录失败信息
			request.setAttribute("errerMsg", "用户名或密码错误");
            // (2)重定向login登录页面,回显错误信息
			request.getRequestDispatcher("/login.jsp").forward(request, response);
		}
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package c8p5;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LogoutServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 1.清除session中保存的用户信息
		request.getSession().removeAttribute("user");
		// 2.清除cookie中保存的用户信息(cookie覆盖)
		Cookie cookie = new Cookie("autoLogin", "");
        cookie.setMaxAge(0);
		cookie.setPath(request.getContextPath());
		response.addCookie(cookie);
        // 3.重定向到index主页面
		response.sendRedirect(request.getContextPath() + "/index.jsp");
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
step4:Servlet(Filter)
package c8p5;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

public class AutoLoginFilter implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
	}
	public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//类型强转为HttpServletRequest对象之后才有getCookies()方法
		HttpServletRequest request = (HttpServletRequest) req;
		// 1.获取所有的cookie信息
		Cookie[] cookies = request.getCookies();
        // 2.遍历cookie,找到保存了用户名与密码的cookie(autoLogin)
		String username_passwd = null;
		for (int i = 0; cookies != null && i < cookies.length; ++i) {
			String cookieName = cookies[i].getName();
			if ("autoLogin".equals(cookieName)) {
				username_passwd = cookies[i].getValue();
				break;
			}
		}
		// 3.如果获取到的用户名与密码不为空,则做自动登录
		if (username_passwd != null) {
			String[] parts = username_passwd.split("-");
			String username = parts[0];
			String password = parts[1];
			// (1)检查用户名和密码,若登录成功则将用户信息user对象存入session域中
			if ("itcast".equals(username) && ("123456").equals(password)) {
				User user = new User();
				user.setUsername(username);
				user.setPassword(password);
				request.getSession().setAttribute("user", user);
			}
		}
		// 3.放行
		chain.doFilter(request, response);
	}
	public void destroy() {
		// TODO Auto-generated method stub
	}
}
step5:xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<!-- c8p5 -->
	<filter>
		<filter-name>AutoLoginFilter</filter-name>
		<filter-class>c8p5.AutoLoginFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>AutoLoginFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<servlet>
		<servlet-name>LoginServlet</servlet-name>
		<servlet-class>c8p5.LoginServlet</servlet-class>	
	</servlet>
	<servlet-mapping>
		<servlet-name>LoginServlet</servlet-name>
		<url-pattern>/LoginServlet</url-pattern>
	</servlet-mapping>
	<servlet>
		<servlet-name>LogoutServlet</servlet-name>
		<servlet-class>c8p5.LogoutServlet</servlet-class>	
	</servlet>
	<servlet-mapping>
		<servlet-name>LogoutServlet</servlet-name>
		<url-pattern>/LogoutServlet</url-pattern>
	</servlet-mapping>
</web-app>
step6:自动登录检验

根据实验目的:在完成首次登录网站后,在一定时间内将能够直接访问index主页而跳过login界面:

测试1:尝试使用url地址直接访问主页(由于没有Cookie登录信息,将显示用户登录超链接)

在这里插入图片描述

测试2:尝试正常首次登录首页

在这里插入图片描述

测试3:尝试使用url地址直接访问主页(由于已经保存了Cookie信息,应自动登录)

在这里插入图片描述

注意:如果在首次登录之后点击了注销,则自动登录功能将失效(Logout.servlet删除客户端中的Cookie)

6.应用2:Filter实现全站编码统一

在web开发中经常会遇到中文乱码问题,根据之前知识可通过在Servlet程序中设置编码方式解决,

请添加图片描述

但如果多个Servlet程序都需要设置编码方式,则将导致大量的重复代码(可以在Filter中对获取到的请求和响应消息进行编码),

从而实现全站编码统一。

请添加图片描述
实现步骤:

  1. 提供form.jsp中文数据提交页面
  2. CharacterFilter对提交的参数进行中文乱码问题的处理
  3. CharacterServlet测试

难点:在于创建CharacterRequest对象,来增强getParameter方法用于处理get请求的中文乱码问题。

step1:form.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="utf-8" import="java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
	<head>
		<title>form.jsp</title>
	</head>
	<body style="text-align: center;">
		<h3>用户登录</h3>
		<a href="<%=request.getContextPath() %>/CharacterServlet?name=lch&password=123456">点击超链接进行登录</a>
		<form action="${pageContext.request.contextPath}/CharacterServlet" method="post">
			<table border="1" width="600px" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
				<tr>
					<td height="30">用户名:</td>
					<td>&nbsp;&nbsp;<input type="text" name="name" />${errerMsg}</td>
				</tr>
				<tr>
					<td height="30">&nbsp;码:</td>
					<td>&nbsp;&nbsp;<input type="password" name="password" /></td>
				</tr>
				<tr>
					<td height="30" colspan="2">
						<input type="submit" value="登录" />&nbsp;&nbsp;&nbsp;&nbsp;
						<input type="reset" value="重置" />
					</td>
				</tr>
			</table>
		</form>
		
	</body>
</html>
step2:Servlet(CharacterServlet)
package c8p6;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CharacterServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println(request.getParameter("name"));
		System.out.println(request.getParameter("password"));
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
step3:Servlet(Filter)
package c8p6;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.management.RuntimeErrorException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

public class CharacterFilter implements Filter {
	public void init(FilterConfig arg0) throws ServletException {}
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest)req;
		HttpServletResponse response = (HttpServletResponse)resp;
		// 1.处理响应中文乱码问题
		request.setCharacterEncoding("text/html; charset = utf-8");
		// 2.处理请求中文乱码问题
		String method = request.getMethod();
		if (method.equalsIgnoreCase("post")) {
			request.setCharacterEncoding("utf-8");
		}
		CharacterRequest characterRequest = new CharacterRequest(request);
		chain.doFilter(characterRequest, response);
	}
	public void destroy() {}
}

// 利用装饰者模式增强getParameter方法
class CharacterRequest extends  HttpServletRequestWrapper {
	//step1.引入被增强的对象
	private HttpServletRequest request;
	public CharacterRequest(HttpServletRequest req) {
		//step2.通过构造方法对引入的对象进行赋值
		super(req);
		this.request = req;
	}
	//step3.增强getParameter方法
	public String getParameter(String name) {
		// 1.调用被包装对象的getParameter()方法,获得请求参数
		String value = super.getParameter(name);
		if (value == null)
			return null;
		// 2.获取请求的方式
		String method = super.getMethod();
		// (1)处理get请求的中文乱码问题方式
		if (method.equalsIgnoreCase("get")) {
			try {
				value = new String(value.getBytes("iso-8859-1"), "utf-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		// 3.解决乱码后返回结果
		return value;
	}
}
step4:xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<!-- c8p6 -->
	<servlet>
		<servlet-name>CharacterServlet</servlet-name>
		<servlet-class>c8p6.CharacterServlet</servlet-class>	
	</servlet>
	<servlet-mapping>
		<servlet-name>CharacterServlet</servlet-name>
		<url-pattern>/CharacterServlet</url-pattern>
	</servlet-mapping>
	<filter>
		<filter-name>CharacterFilter</filter-name>
		<filter-class>c8p6.CharacterFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CharacterFilter</filter-name>
		<url-pattern>/7</url-pattern>
	</filter-mapping>
</web-app>
step5:统一编码测试

POST方式提交测试:在form表单中输入账号与密码信息后,点击提交按钮进行数据提交,查看控制台输出:

在这里插入图片描述

GET方式提交测试:在form.jsp页面中直接点击超链接,进行直接登录,查看控制台输出信息:

在这里插入图片描述

总结:两次输出结果相同都没有出现乱码问题,说明Filter过滤器快捷的完成了统一编码的功能。

二、Listener监听器

1.Listener监听器概述:

在程序开发中需要对某些事件进行监听,监听器在监听的过程中主要有以下几个部分

  1. 事件(Event):用户的任意一个操作
  2. 事件源:产生事件的对象
  3. 事件监听器(Listener):负责监听发生在事件源上的事件
  4. 事件处理器:监听器的成员方法,当事件发生时会触发对应的处理器(成员方法)

事件处理器在进行工作时,可分为几个步骤具体如下:

步骤说明
step1:注册监听器将监听器绑定到事件源
step2:传递事件对象事件发生时会触发监听器的成员方法(事件处理器)
step3:处理事件源事件处理器通过事件对象获得事件源,并对进行处理

在Web开发中使用的监听器(Servlet事件监听器),就是一个实现了特定接口的Java程序

专门用于监听Web应用程序中ServletContext、HttpSession和ServletRequest等域对象的创建与销毁、监听域对象属性的修改,

及感知HttpSession域中某个对象的状态,具体如下图所示:

在这里插入图片描述

注意:HttpSessionActivationListener用于监听HttpSession中对象活化和钝化的过程

Web服务器会根据监听器实现的接口,将其注册到被监听的对象上,

当触发了某个对象的监听事件时,Web容器将会调用Servlet监听器与之相关的方法对事件进行处理。

2.应用1:监听域对象的生命周期:

要对Servlet域对象的生命周期进行监听,

首先要实现域对象对应的ServletContextListener、HttpSessionListener和ServletRequestListener3个接口,

这些接口中的方法和执行过程非常类似,可以为每一个监听器编写一个单独的类,也可以用一个类实现这3个接口(从而让这个类具有3个事件监听器的功能)。

step1:myjsp.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
	<head>
		<title>this is MyJsp.jsp page</title>
	</head>
	<body>
		这是一个测试监听器页面!
	</body>
</html>
step2:Listener
package c8p7;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyListener implements ServletContextListener, HttpSessionListener, ServletRequestListener {
	public void contextInitialized(ServletContextEvent arg0) {
		System.out.println("ServletContext对象被创建了!");
	}
	public void contextDestroyed(ServletContextEvent arg0) {
		System.out.println("ServletContext对象被销毁了!");
	}
	public void requestInitialized(ServletRequestEvent arg0) {
		System.out.println("ServletRequest对象被创建了!");
	}
	public void requestDestroyed(ServletRequestEvent arg0) {
		System.out.println("ServletRequest对象被销毁了!");
	}
	public void sessionCreated(HttpSessionEvent arg0) {
		System.out.println("HttpSession对象被创建了!");
	}
	public void sessionDestroyed(HttpSessionEvent arg0) {
		System.out.println("HttpSession对象被销毁了!");
	}
}
step3:xml配置
<!-- c8p7 -->
<listener>
    <listener-class>c8p7.MyListener</listener-class>
</listener>
<session-config>
    <session-timeout>1</session-timeout>
</session-config>
step4:生命周期监听测试

在tomcat启动服务器后,通过浏览器访问myjsp.jsp页面,并同时观察eclipse控制台的输出情况如下:

在这里插入图片描述

在这里插入图片描述

一分钟后,ServletSession对象也被销毁了(调用Destroyed方法)

在这里插入图片描述

3.应用2:监听域对象的属性变更:

通过所学监听器知识,读者应学会使用监听器监听域对象的属性变更。

step1:testAttribute.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
	<head>
		<title>testAttribute</title>
	</head>
	<body>
		<h5>这是一个测试对象属性信息监听器的页面</h5>
		<%
			getServletContext().setAttribute("username", "lch");
			getServletContext().setAttribute("username", "luochenhao");
			getServletContext().removeAttribute("username");
			session.setAttribute("username", "lch");
			session.setAttribute("username", "luochenhao");
			session.removeAttribute("username");
			request.setAttribute("username", "lch");
			request.setAttribute("username", "luochenhao");
			request.removeAttribute("username");
		%>
	</body>
</html>
step2:Listener
package c8p8;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class MyAttributeListener implements ServletContextAttributeListener, ServletRequestAttributeListener, HttpSessionAttributeListener {
	// 1.ServletContext属性监听
	public void attributeAdded(ServletContextAttributeEvent sae) {
		String name = sae.getName();
		System.out.println("ServletContext添加属性:" + name + "=" + sae.getServletContext().getAttribute(name));
	}
	public void attributeRemoved(ServletContextAttributeEvent sae) {
		String name = sae.getName();
		System.out.println("ServletContext移除属性:" + name + "=" + name);
	}
	public void attributeReplaced(ServletContextAttributeEvent sae) {
		String name = sae.getName();
		System.out.println("ServletContext替换属性:" + name + "=" + sae.getServletContext().getAttribute(name));
	}
	// 2.HttpSession属性监听
	public void attributeAdded(HttpSessionBindingEvent hbe) {
		String name = hbe.getName();
		System.out.println("HttpSession添加属性:" + name + "=" + hbe.getSession().getAttribute(name));
	}
	public void attributeRemoved(HttpSessionBindingEvent hbe) {
		String name = hbe.getName();
		System.out.println("HttpSession移除属性:" + name);
	}
	public void attributeReplaced(HttpSessionBindingEvent hbe) {
		String name = hbe.getName();
		System.out.println("HttpSession替换属性:" + name + "=" + hbe.getSession().getAttribute(name));
	}
	// 3.ServletRequest属性监听
	public void attributeAdded(ServletRequestAttributeEvent srae) {
		String name = srae.getName();
		System.out.println("ServletRequest添加属性:" + name + "=" + srae.getServletRequest().getAttribute(name));
	}
	public void attributeRemoved(ServletRequestAttributeEvent srae) {
		String name = srae.getName();
		System.out.println("ServletRequest移除属性:" + name);
	}
	public void attributeReplaced(ServletRequestAttributeEvent srae) {
		String name = srae.getName();
		System.out.println("ServletRequest替换属性:" + name + "=" + srae.getServletRequest().getAttribute(name));
	}
}
step3:xml配置
<!-- c8p8 -->
<listener>
		<listener-class>c8p8.MyAttributeListener</listener-class>
</listener>
step4:属性监听测试

在tomcat启动服务器后,通过浏览器访问testAttribute.jsp页面,并同时观察eclipse控制台的输出情况如下:

在这里插入图片描述
在这里插入图片描述

可以看出在三个域对象中,分别完成了属性增加、替换与删除的监听操作

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值