Servlet技术规范 描述三种技术 : Servlet(服务器小程序) 、Filter(过滤器)、 Listener(监听器)
1 新建类 继承httpServlet
2 重写doGet 和 dopost方法
3 在web.xml中 注册和映射

  1. Filter简介
    Filter也称之为过滤器,它是Servlet技术中最实用的技术,
    例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,
    从而实现一些特殊的功能。例如实现URL级别的权限访问控制、
    过滤敏感词汇、解决get和post乱码等一些高级功能。
    Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。
    通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,
    对访问的请求和响应进行拦截,如下所示:
    过滤器执行中的位置

2.Filter是如何实现拦截的?
Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源(拦截url)进行拦截后,WEB服务器每次在调用web资源之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行
是否调用目标资源(即是否让用户访问web资源)。
web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
调用目标资源之后,让一段代码执行

原理分析

Filter运行在服务器端,对服务器端web资源的访问 进行拦截,起到过滤的作用

Servlet API中 定义接口 Filter,用户只需要编写程序实现Filter接口,完成过滤器编写

  1. Filter快速入门
    3.1. 准备工作
    hello.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Hello,JSP</h1>
</body>
</html>
// HelloServlet
public class HelloServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.getWriter().println("<h1>Hello,Servlet</h1>");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}

3.2. 编写类 实现 Filter接口

/**
 * 编写为过滤器,过滤器作用 用来拦截web资源的访问
 * 
 * @author seawind
 * 
 */
public class Filter1 implements Filter {

	@Override
	public void destroy() {
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// 执行过滤
		System.out.println("执行Filter1 ");

		// 拦截后,如何让目标资源执行
		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}
}

3.3. 在服务器端注Filter(配置拦截哪个web资源----- web.xml

 <!-- 注册过滤器 -->
  <filter>
  	<filter-name>Filter1</filter-name>
  	<filter-class>cn.itcast.filter.Filter1</filter-class>
  </filter>	
  
  <!-- 配置过滤器去拦截哪个资源 -->
  <filter-mapping>
  	<filter-name>Filter1</filter-name>
  	<url-pattern>/hello.jsp</url-pattern>
  	<!-- 拦截/hello是Servlet 路径 -->
	<!--  	<url-pattern>/hello</url-pattern>-->
    <!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
    <servlet-name>HelloServlet</servlet-name>
  </filter-mapping>

3.4. 客户端访问被拦截目标资源之前,服务器调用Filter的doFilter方法 ,执行过滤

3.5. Filter的doFilter方法中传入 FilterChain, 如果调用FilterChain的doFilter 就会执行目标资源,否则目标资源不会执行

放行方法

问题1: 每次使用过滤器都需要 实现filter接口, 再去web.xml中配置, 是否很麻烦? 有没有更加简便的方法?
问题2: 如果配置两个过滤器谁先执行,谁后执行? 由什么决定的?
4. FilterChain

多个过滤器组成过滤器链,谁先执行,谁后执行?

在客户端访问服务器web资源时,服务器端为一个web资源,配置多个过滤器拦截 ,这多个过滤器,就会组成过滤器链 FilterChain, 调用FilterChain的doFilter 表示要执行过滤器链下一个资源,如果当前过滤器已经是链上最后一个过滤器,就会执行目标资源

  • web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter
  1. Filter生命周期
    我相信在北京大家都有乘坐地铁的经历, 为了安全期间, 我们必须安检部门的检查, 通过后才可以乘坐地铁.
    1 乘坐地铁服务 对应 服务器上资源服务
    2 安检部门 对应 过滤器

安检部门上岗前, 必须 发放安检设备
每次安检,都有严格的流程
结束安检, 需要回收安检设备

public class AnjianFilter implements Filter {
    public AnjianFilter() {
    	System.out.println("构造方法: 地铁安检部门成立----------------------");
    }
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("init: 安检人员上岗前的准备工作----------------------");
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("doFilter: 每次有人进入地铁必须进行安全检查----------------------" + new Date().toLocaleString());
		// 允许通过
		chain.doFilter(request, response);
	}
	
	public void destroy() {
		System.out.println("destroy: 收回安检设备----------------------");
	}
}

init(FilterConfig) doFilter(request,response,filterChain) destroy()
1、Filter对象在tomcat服务器启动时 创建,调用init方法 (只会创建一个对象,init方法执行一次)
2、doFilter 每次拦截目标资源时,执行
3、destroy 服务器关闭时执行

请思考:
安检部门 如果在非节假日客流量少时,5个手持安检设备就够了.
但是碰上节假日客流量多时,10个手持安检设备才够用,需要向上级申请.
节假日过后客流量少了,4个手持安检设备就够了,还需要再次向上级申请换回去…
如此频繁的申请,等待通过是否很麻烦,大家有没有好的主意?
6. FilterConfig对象
FilterConfig 作用和 ServletConfig 类似,用来在Filter初始化阶段,将参数传递给过滤器
1、通过 String getInitParameter(String name) 获得过滤器初始化参数

  • FilterConfig 提供参数,是Filter类私有参数,Filter2的初始化参数,不能在Filter1 中进行获取
  • 配置全局参数, 进行配置,通过ServletContext 获得
    这里写图片描述
    这里写图片描述
  1. 过滤器拦截配置

这里写图片描述

这里写图片描述

1、如果连接目标资源是一个Servlet,可以选择url和servlet名称两种配置方式

<!-- 拦截/hello是Servlet 路径 -->
<url-pattern>/hello</url-pattern>
<!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
<servlet-name>HelloServlet</servlet-name>

2、url-pattern 和 Servlet中路径写法一样,有三种 : 完全匹配、目录匹配、扩展名匹配
3、指定过滤器所拦截的资源被 Servlet 容器调用的方式
容器调用服务器端资源 有四种方式
REQUEST、FORWARD、INCLUDE、ERROR

  1. Filter应用
    8.1. 案例1:解决post方式获取参数中文乱码的编码过滤器
    案例:编写jsp 输入用户名,在Servlet中获取用户名,将用户名输出到浏览器上
// 处理请求post乱码代码
request.setCharacterEncoding("utf-8");
// 设置响应编码集代码`这里写代码片`
response.setContentType("text/html;charset=utf-8");

经常会使用,而过滤器可以在目标资源之前执行,将很多程序中处理乱码公共代码,提取到过滤器中 ,以后程序中不需要处理编码问题

需求: 要求用户输入用户名,显示在页面上

8.1.1. input.jsp

<form action="${pageContext.request.contextPath }/demo1" method="post">
	请输入你的大名<input type="text" name="username"/><br/>
	<input type="submit" value="提交"/>
</form>

8.1.2. Demo1Serlvet

public class Demo1Servlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String username = request.getParameter("username");
		response.getWriter().println("幸会," + username);
	}
}

8.1.3. EncodingFilter

public class EncodingFilter implements Filter {
	public EncodingFilter() {
	}

	public void destroy() {
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 解决获取参数出现中文乱码
		request.setCharacterEncoding("utf-8");
		// 解决浏览器出现中文乱码
		response.setContentType("text/html;charset=utf-8");
		// 放行
		chain.doFilter(request, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
	}
}

案例1缺点: 可以解决post方式的乱码, 但是解决get方式的乱码吗? 如果不可以请思考怎么做?
案例2:解决整个项目的乱码

<form action="/day16/demo2Servlet" method="post">
	昵称<input type="text" name="nickname"/><br/>
	城市<input type="text" name="city"/><br/>
	<input type="submit" value="提交"/>
</form>
public class GenericEncodingFilter implements Filter {
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 为了能够使用子类的方法,需要向下转型
		HttpServletRequest httpServletRequest = (HttpServletRequest)request;
		// 使用包装 对方法进行增强
		HttpServletRequest myRequest = new MyRequest(httpServletRequest);
		// 放行
		chain.doFilter(myRequest, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
	}

	@Override
	public void destroy() {
		
	}
	
	class MyRequest extends HttpServletRequestWrapper{
		private HttpServletRequest httpServletRequest;
		
		// 记录是否修改过
		private boolean isUpdate = false;
		
		public MyRequest(HttpServletRequest request) {
			super(request);
			this.httpServletRequest = request;
		}
		
		@Override
		public Map<String, String[]> getParameterMap() {
			// 1 获取请求方式
			String method = httpServletRequest.getMethod();
			// 2 根据不同方式,做出对应的处理
			if("post".equalsIgnoreCase(method)) {
				// 设置请求编码集
				try {
					httpServletRequest.setCharacterEncoding("utf-8");
					return httpServletRequest.getParameterMap();
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}else if("get".equalsIgnoreCase(method)) {
				// 先编码,再解码
				Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
				if(!isUpdate) {
					try {
						for(Entry<String, String[]> entry : parameterMap.entrySet()){
							String[] valueArr = entry.getValue();
							for(int i=0; i<valueArr.length; i++) {
								valueArr[i] = new String(valueArr[i].getBytes("iso-8859-1"), "utf-8");
							}
						}
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
					isUpdate = true;
				}
				return parameterMap;
			}
			return super.getParameterMap();
		}
		
		@Override
		public String getParameter(String name) {
			Map<String, String[]> parameterMap = this.getParameterMap();
			String[] valueArr = parameterMap.get(name);
			if(valueArr!=null) {
				return valueArr[0];
			}else {
				return null;
			}
		}
		@Override
		public String[] getParameterValues(String name) {
			return this.getParameterMap().get(name);
		}
	}
}

==================================================================================================================
过滤器
1、过滤器编写步骤
2、全局编码 过滤器
3、通用get/post乱码过滤器 ------------ 理解实现过程 ,保存起来会使用

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐