过滤器介绍

什么是过滤器

生活中的例子:
滤网,筛子,渔网
生活中的过滤器:排除我们不需要的东西,留下,需要的。

高考:只有分数够高的同学才能进入理想的大学。有一部分同学被拦截在大学之外。(起到拦截的作用)

极客营:一开始大家都是小白,进入极客营学习,经历了5个月的学习,毕业之后,具有了一定的编码能力。
(对每一个经过的学员,都增强了学员的编码能力,起到了增强的作用)

JavaWeb中的过滤器的概念:对请求和响应进行拦截或者增强的对象,就是过滤器。(重点)

JavaWeb中的过滤器是什么呢?
Filter接口:功能——对请求和响应进行增强,或者进行拦截。
image.png

JavaWEB中的过滤器运行图解(重点)

image.png

Filter的快速入门(重点:必须掌握)

Filter定义以及创建步骤介绍

定义一个过滤器:
第一步:创建一个类,实现过滤器接口
第二步:具体需要执行的过滤任务,写在doFilter
* 第三步:过滤器需要在web.xml中配置

代码演示:

  1. package cn.igeek.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. /**
  10. *
  11. * Filter 是在 Web 应用程序的部署描述符中配置的
  12. *
  13. * 部署描述符:web.xml
  14. *
  15. * 定义一个过滤器:
  16. * 第一步:创建一个类,实现过滤器接口
  17. * 第二步:具体需要执行的过滤任务,写在doFilter
  18. * 第三步:过滤器需要在web.xml中配置
  19. * */
  20. public class MyFilter implements Filter{
  21. /**
  22. * 初始化方法
  23. * */
  24. @Override
  25. public void init(FilterConfig filterConfig) throws ServletException {
  26. // TODO Auto-generated method stub
  27. }
  28. /**
  29. * 执行过滤任务的方法
  30. * */
  31. @Override
  32. public void doFilter(ServletRequest request, ServletResponse response,
  33. FilterChain chain) throws IOException, ServletException {
  34. System.out.println("MyFilter....doFilter.....");
  35. }
  36. /**
  37. * 销毁的方法
  38. * */
  39. @Override
  40. public void destroy() {
  41. // TODO Auto-generated method stub
  42. }
  43. }

Filter 是在 Web 应用程序的部署描述符中配置的——过滤器创建好之后,需要在web.xml中做配置

在web.xml文件中配置过滤器

  1. <filter>
  2. <filter-name>MyFilter</filter-name>
  3. <filter-class>cn.igeek.filter.MyFilter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>MyFilter</filter-name>
  7. <url-pattern>/1.txt</url-pattern>
  8. </filter-mapping>

Filter拦截操作效果

image.png

过滤器放行的对象:FilterChain功能介绍

image.png
总结:过滤放行,全靠它

FilterChain的doFilter方法
image.png
代码实现
image.png
效果:
过滤器放行执行过程:
image.png

Filter生命周期

回顾servlet的生命周期:
创建:第一次被访问的时候
销毁:服务器关闭的时候,或者当前项目从服务器中移除

回顾session的生命周期:
创建:第一次调用getsession方法,第一次访问jsp
销毁:服务器非正常关闭,超过生存时间,调用销毁的方法

Filter:
创建:在服务器启动的时候

服务器启动截图:
image.png
销毁:在服务器关闭的时候,过滤器销毁。
服务器关闭截图:
image.png

FilterConfig介绍

servletConfig对象:获取servlet相关的配置信息。

FilterConfig定义:获取filter相关的配置信息。
image.png
API介绍:
image.png
API代码演示:
1)设置过滤器初始化参数
2)通过filterconfig对象来获取参数
image.png
参数配置:

<filter>
      <filter-name>MyFilter</filter-name>
      <filter-class>cn.igeek.filter.MyFilter</filter-class>
      <init-param>
          <param-name>haha</param-name>
          <param-value>哈哈</param-value>
      </init-param>
      <init-param>
          <param-name>heihei</param-name>
          <param-value>嘿嘿</param-value>
      </init-param>
  </filter>

  <filter-mapping>
      <filter-name>MyFilter</filter-name>
      <url-pattern>/1.txt</url-pattern>
  </filter-mapping>

效果演示:
image.png

Filter配置详解(web.xml中的配置)(重点)

关于url-pattern配置


过滤器如何匹配请求的路径?
回顾servlet的url-pattern:
全路径匹配——
地址栏:localhost:8080/项目根路径/资源路径 localhost:8080/igeek-filter2/1.txt
通配符的匹配——
地址栏:localhost:8080/项目根路径/abc/
以上两种匹配方式,配置路径的时候必须以”/”开头
image.png
后缀名匹配——
.do: .do .txt .action
地址栏:localhost:8080/项目根路径/
.txt
后缀名匹配方式,配置路径的时候不能以”/”开头
Filter的url-pattern配置与servlet一致。

过滤器的执行顺序?
测试方式:
1)两个过滤器,拦截同一个请求
2)调整两个过滤器的配置,再来看执行的顺序
总结:
过滤器执行的顺序是按照,web.xml中filter-mapping标签的书写顺序执行(从上往下执行)

关于filter-name配置

什么是filter-name配置?
定义:针对指定的servlet进行拦截或者增强操作的配置

Servlet:

package cn.igeek.servlet;

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 DemoServlet extends HttpServlet {

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

        System.out.println("DemoServlet....doGet.....");
    }

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

}

Filter:

package cn.igeek.filter;

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 DemoFilter implements Filter{

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("DemoFilter.....doFilter....");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }

}

Web.xml配置:

<filter>
        <filter-name>demoFilter</filter-name>
        <filter-class>cn.igeek.filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>demoFilter</filter-name>
        <url-pattern>/demo</url-pattern>
    </filter-mapping>

效果:
image.png

案例2—解决shop项目中乱码

需求:请求参数在每一个servlet中单独中文乱码处理,代码重复
image.png
image.png
优化的思路,使用一个过滤器,在请求到达servlet之前,先对request对象进行设置编码
image.png
要对所有的请求都要进行设置编码,都要拦截,进行增强,url-pattern:/*

过滤器代码:

package cn.igeek.filter;

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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class EncodingFilter implements Filter{

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;
        //处理乱码,处理post请求乱码
        req.setCharacterEncoding("utf-8");
        chain.doFilter(req, res);

    }

    @Override
    public void destroy() {
    }

}

Web.xml配置:
image.png

实现完整版的乱码处理,过滤器:(了解,不做要求)

第一步:还以要有一个过滤器,对请求进行乱码处理的任务(增强任务),写在doFilter方法中

补充(装饰(包装)设计模式心法):
1) 定义一个类,实现被装饰对象的接口
2) 定义一个成员变量,记住被装饰对象的引用
3) 定义构造方法,传入被装饰对象的实例
4) 改写要修改的方法
5) 不需要改写的方法,调用被装饰对象的原来的方法

复杂过滤器实现:

package cn.igeek.filter;

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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.igeek.domain.MyRequest;

public class EncodingFilter implements Filter{

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 对请求和响应,处理中文乱码
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;

        res.setContentType("text/html;charset=utf-8");


        //针对不同的请求方式,处理乱码,使用一个新的request对象,在新的request对象中,处理乱码
        //设置构造函数有req对象,是为了保证,获取到请求中的数据,然后处理乱码
        MyRequest myRequest = new MyRequest(req);
        //测试获取请求参数
        System.out.println("name1:"+myRequest.getParameter("name"));
        System.out.println("name2:"+myRequest.getParameter("name"));

        //处理完成请求和响应乱码,只需要放行
        chain.doFilter(myRequest, res);

    }

    @Override
    public void destroy() {
    }

}

自定义增强类:

package cn.igeek.domain;

import java.io.UnsupportedEncodingException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * @author 王嘉男
 *
 * @time 2017年5月26日
 * 
 * MyRequest如何实现:
 * 
 * 1 实现httpservletrequest接口——通过继承HttpServletRequestWrapper
 * 2 设置一个变量,用来保存服务器创建的request对象
 * 3 处理乱码,就是改写获取请求参数的三个方法
 * 4 不需要改写的方法,使用原来的对象方法
 */
public class MyRequest extends HttpServletRequestWrapper{
    //为了后期可以使用服务器创建的request对象中的请求参数
    //设置一个变量,用来保存服务器创建的request对象
    private HttpServletRequest request ;
    //定义一个标记,控制编码的次数,只执行一次
    private boolean flag = false;
    public MyRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    public Map<String, String[]> getParameterMap() {

        //获取请求方式,根据不同方式处理乱码
        String method = request.getMethod();
        if(method.equalsIgnoreCase("post")){
            try {
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return super.getParameterMap();
            }
        }else if(method.equalsIgnoreCase("get")){

            //将所有的请求参数获取出来,然后,一个一个地处理乱码
            Map<String, String[]> map = request.getParameterMap();
            //如果控制编码,让他只执行一次?
            //flag 默认是false,所以往下执行
            //flag 第二次执行,true ,直接结束,不在执行。
            if(flag){
                return map;
            }
            //变量map集合获取数据
            if(map != null){
                for(String key :map.keySet()){
                    String[] values = map.get(key);
                    if(values != null){
                        for (int i = 0; i < values.length; i++) {
                            try {
                                String string = new String(values[i].getBytes("iso-8859-1"),"utf-8");
                                values[i] = string;
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                                //如果出异常,希望后边,还没有循环到的数据,继续处理乱码
                                //结束当前循环,开启下一个循环
                                continue;
                            }
                        }
                    }
                }
            }
            flag = true;
            return map;
        }else{
            return super.getParameterMap();
        }
    }

    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> map = this.getParameterMap();
        if(map != null){
            String[] values = map.get(name);
            return values;
        }
        return super.getParameterValues(name);
    }

    @Override
    public String getParameter(String name) {
        String[] values = this.getParameterValues(name);
        if(values != null){
            return values[0];
        }
        return super.getParameter(name);
    }


}

注意:最后还有去掉原来设置编码的代码。