likes
comments
collection
share

深入了解过滤器、监听器(中)

作者站长头像
站长
· 阅读数 17

第四章 Filter的生命周期

我们在前面的文章学习过servlet生命周期,并且上面,我们分析了Filter的执行流程。我们有没有想过,我们并没有向Filter发送请求,请求是怎么进入到doFilter()中的呢?Filter类的对象是在什么时候创建的呢?创建之后又是怎么工作的呢?最后是在什么时候销毁的呢?这些问题就是Filter的生命周期问题。接下来我们要学习过滤器filter的生命周期,和servlet的生命周期差不多。

深入了解过滤器、监听器(中)

1.filter生命周期代码

接下来我们可以通过以下代码对Filter的生命周期问题进行验证:

【验证Filter的生命周期】LifecycleFilter.java

@WebFilter("/lifecycle")
public class LifecycleFilter implements Filter {
    @Override
    public void destroy() {
        System.out.println("被销毁了。。。");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        //your  code....
        System.out.println("doFilter()被执行了。。。");
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        System.out.println("初始化了。。。");
    }

    public LifecycleFilter() {
        System.out.println("构造方法被执行了。。。");
    }
}

重启服务器:在浏览器输入访问过滤器地址,并查看控制台。

http://localhost:8080/lifecycle

深入了解过滤器、监听器(中)

【结论】

  1. Filter类的对象是在服务器启动的时候创建的;
  2. Filter类对象创建完成后会调用init()方法;
  3. 每当一个请求的路径是满足过滤器的配置路径,那么就会执行一次过滤器的doFilter方法 (类似于service方法)
  4. 当服务器停止的时候,销毁过滤器,执行过滤器的destory方法

2.doFilter()方法

doFilter()方法是拦截请求的方法,只要一个请求符合Filter拦截路径,都会被doFilter()方法拦截。doFilter()方法有3个参数,这3个参数的作用分别如下:

参数说明
ServletRequest reqServletRequest是HttpServletRequest类的父接口,其中包含了被拦截的请求的信息
ServletResponse respServletResponse是HttpServletResponse类的父接口,其中包含了被拦截的响应的信息
FilterChain chainFilterChain给Filter提供了放行的方法。chain.doFilter();

FilterChain 是一个接口,里面有doFilter(req,resp)方法,作用是将浏览器提交请求对象和响应对象传到过滤器的资源里面去,所以doFilter()方法具有放行的作用。

3.destory()方法

过滤器的销毁方法。服务器停止的时候,销毁过滤器,调用一次destory方法。

【测试】在destory()方法中输出一句话,如下:

    @Override
    public void destroy() {
      System.out.println("服务器关闭");
        System.out.println("LifecycleFilter的destory方法执行了.......................");
    }

【关闭tomcat】查看结果

深入了解过滤器、监听器(中)

【结论】

  1. 过滤器是在服务器停止时销毁的;
  2. 过滤器销毁后会调用destory()方法;

【小结】Filter生命周期小结

  1. 当服务器启动的时候,过滤器就被初始化了,执行过滤器的init方法
  2. 每当一个请求的路径是满足过滤器的配置路径,那么就会执行一次过滤器的doFilter方法
  3. 当服务器停止的时候,销毁过滤器,执行过滤器的destory方法

第五章 Filter的映射路径

映射路径指的是当前这个Filter需要拦截的路径。这个路径的配置方式有3种:

  1. 精确匹配:匹配指定的urlpattern;
  2. 模糊匹配:匹配复合条件的一类路径;
  3. 多路径匹配:一个filter匹配多个资源;

1、精确匹配

精确匹配,要求访问的资源路径必须与Filter的urlPatterns中的配置一致。如下:

精确匹配说明
urlPatterns = "/index.jsp"拦截对index.jsp页面的请求
urlPatterns = "/loginServlet"拦截对loginServlet的请求
urlPatterns = "/a.jsp"拦截对a.jsp的请求

2、模糊匹配

有时候,我们需要对这个服务器中的所有请求都有所特殊处理,比如处理post请求的乱码问题。我们就需要对所有的请求都设置乱码处理。这时候,就需要对所有的请求进行拦截。如果通过上面的精确匹配模式,需要配置很多的请求,这样就比较麻烦了。如果通过urlPatterns的模糊匹配,就能够对这些请求进行统一处理。

模糊匹配主要有以下3种写法:

模糊匹配使用示例说明
urlPatterns = "/*"urlPatterns = "/*"对所有的请求都进行拦截
urlPatterns = "/xxx/*"urlPatterns = "/user/*"对映射路径中/user下的所有资源进行拦截
urlPatterns = "*.xxx"urlPatterns = "*.jsp"对指定后缀的请求进行拦截 不加/

使用场景

深入了解过滤器、监听器(中)

今天电商狂欢日,产品经理要求对所有购买的电脑进行打9折

http://localhost:8080/computer/dell

http://localhost:8080/computer/xiaomi

http://localhost:8080/computer/thinkpad

效果:我们只要过滤器中路径设置为:/computer/*,那么任何电脑的请求都会被过滤器拦截。

使用场景

网站有很多种请求:浏览商品,购买商品,删除商品;用户登录,修改用户信息,注册用户信息。

我们可以将请求分为两类:xxx.action和xxx.do

这样可以分清楚网站请求的类别。

效果:我们只要在过滤器中设置路径为:*.action,那么,所有以.action结尾的请求都会被过滤器拦截。

代码演示如下:

对所有的请求都进行拦截:

@WebFilter("/*")
public class OneFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("OneFilter执行了。。。");
//        chain.doFilter(request, response);
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

对映射路径中/user下的所有资源进行拦截:

@WebFilter("/user/*")
public class TwoFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("TwoFilter执行了。。。。。");
//        chain.doFilter(request, response);
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

对指定后缀的请求进行拦截:

@WebFilter("*.action")
public class ThrFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("ThrFilter执行了。。。。");
//        chain.doFilter(request, response);
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

3、多个路径匹配

一个过滤器可以匹配多个路径

xml实现方式:

深入了解过滤器、监听器(中)

注解实现方式:

深入了解过滤器、监听器(中)

温馨提示:路径没有/,Tomcat会报错!

深入了解过滤器、监听器(中)

第六章 过滤器的拦截方式

什么是拦截方式:对浏览器或服务器过来的请求进行拦截。

1.通过浏览器发送的请求:默认的
2.请求转发:默认不拦截

案例:演示过滤器的拦截方式。分别从浏览器和服务器转发过来的请求。

【代码如下】

HelloServlet:

@WebServlet("/helloServlet")
public class HelloServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("helloServlet 接收了请求。。。。。");
        //响应数据
        response.getWriter().print("hello");
    }
}

HelloFilter:

@WebFilter("/helloServlet")
public class HelloFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("HelloFilter拦截了请求。。。。");
        chain.doFilter(request, response);
        System.out.println("HelloFilter拦截了响应。。。。");
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

1. 默认的拦截方式

只对从浏览器直接发送过来的请求进行拦截,值:REQUEST

在浏览器中直接访问HelloServlet,看过滤器是否拦截。

深入了解过滤器、监听器(中)

显示是拦截了。

2. 来自转发的请求被拦截

  1. 在filter.jsp转发到HelloServlet,查看过滤器是否拦截。

filter.jsp页面代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
   <%-- <jsp:forward page="/helloServlet"/>--%>
     <%
      request.getRequestDispatcher("/helloServlet").forward(request, response);
     %>
</body>
</html>

在浏览器上直接访问filter.jsp页面。 深入了解过滤器、监听器(中)

idea控制台:

深入了解过滤器、监听器(中)

我们发现直接访问jsp,然后在jsp页面转发访问helloServlet,过滤器没有拦截。

因为过滤器默认只拦截从浏览器的直接请求。不拦截请求转发的请求,如果希望过滤器对请求转发的请求进行拦截,我们必须按照如下进行配置。

2.过滤器的配置

配置方式1:web.xml

    <!--配置一个过滤器-->
    <filter>
        <!--过滤器名字-->
        <filter-name>hello</filter-name>
        <!--过滤器的类全名-->
        <filter-class>com.ita.demo01.HelloFilter</filter-class>
    </filter>
    <filter-mapping>
        <!--过滤器名字,必须与上面的名字相同-->
        <filter-name>hello</filter-name>
        <!--过滤的地址-->
        <url-pattern>/helloServlet</url-pattern>
        <!--配置拦截方式,为转发-->
        <dispatcher>FORWARD</dispatcher>
        <!--配置拦截方式,浏览器的请求-->
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

说明:

1)在web.xml中配置之后必须去掉过滤器中的注解

//@WebFilter("/helloServlet")
public class HelloFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("HelloFilter拦截了请求。。。。");
        chain.doFilter(request, response);
        System.out.println("HelloFilter拦截了响应。。。。");
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

2)如果只配置 FORWARD 只会拦截转发的请求,必须还得配置 REQUEST同时还得拦截浏览器的请求。

配置方式2:注解

@WebFilter(urlPatterns = "/helloServlet", dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
public class HelloFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("HelloFilter拦截了请求。。。。");
        chain.doFilter(request, response);
        System.out.println("HelloFilter拦截了响应。。。。");
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

注意:去掉web.xml文件中的拦截代码。

3 拦截类型小结

拦截类型作用
REQUEST只对直接来源于浏览器的请求才过滤(包含重定向)
FORWARD对服务器的转发进行过滤

第七章 Filter应用案例(练习一下叭)

1、Filter统一解决post请求乱码问题

8.0版本以上的tomcat为我们解决了get请求的乱码问题。我们需要自己处理post请求的乱码问题,因为我们的一个项目中可能有很多的请求。当然,我们可以在每一个Servlet中都对post的乱码进行处理。但是,Filter给我们提供了一个更加便捷的方式。我们自定义一个Filter类,拦截所有的请求,然后对这些请求进行post乱码处理即可。

【参考代码】:

1)页面:register.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>form</title>
</head>
<body>
<form action="/registerServlet" method="post">
    用户名:<input type="text" name="name"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

2)后台 servlet代码:

RegisterServlet.java

@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //处理请求乱码
        //request.setCharacterEncoding("utf-8");
        //处理响应乱码
        //response.setContentType("text/html;charset=utf-8");
        /*
            在这里我们需要处理post请求乱码和响应乱码,如果多个servlet,那么我们就会每个servlet
            中都会进行处理,这样代码会冗余,我们可以考虑使用过滤器来解决处理乱码问题
         */
        //获取用户数据
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        System.out.println("name = " + name);
        response.getWriter().println("哈哈,乱码解决楼!");
    }
}

说明:上述代码中我们需要处理post请求乱码和响应乱码,如果多个servlet,那么我们就会每个servlet中都会进行处理,这样代码会冗余,我们可以考虑使用过滤器来解决处理乱码问题。即将处理乱码的代码放到过滤器中来处理。

3)过滤器EncodingFilter中的代码:

@WebFilter("/*")
public class EncodingFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        //统一处理post请求乱码
        request.setCharacterEncoding("utf-8");
        //统一处理响应乱码
        response.setContentType("text/html;charset=utf-8");
        //放行
        chain.doFilter(request, response);
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

2、过滤器案例--登录权限校验

【需求】

  1. 登录之后:能够访问hack.html,然后下载黑客视频资源,成为NB黑客;
  2. 未登录:跳转到登录页面,并提示"请先登录";
  3. 我们对hack.html页面进行过滤,

详细描述:

1.页面:hack.html 访问这个页面要求必须先登录,不登录不能访问,使用过滤器书写代码让其跳转到登录页面login.html.

2.登录LoginServlet中获取用户名和密码,存放到User对象中,然后在session中。最后重定向到hack.html

3.使用一个过滤器对hack.html 进行拦截,先从session中获取用户信息,如果没有登录,获取的是null。跳转到登录页面。如果不为null,说明登录了,可以访问hack.html。

下载资源页面:hack.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>黑客。。</h1>
<a href="javascript:;">下载黑客资源</a>
</body>
</html>

页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 <form action="/loginServlet" method="post">
     用户名:<input type="text" name="name"><br>
     密码:<input type="password" name="password"><br>
     <input type="submit" value="登录">
 </form>
</body>
</html>

javabean,User类:

public class User {
    private String name;
    private String password;

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", password='" + password + ''' +
                '}';
    }
}

LoginServlet代码:

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取用户名和密码
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        User user = new User(name,password);
        System.out.println("user = " + user);
        //将用户信息保存session中
        request.getSession().setAttribute("user",user);
        response.sendRedirect("/hack.html");
    }
}

LoginFilter代码:

@WebFilter("/hack.html")
public class LoginFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        //获取用户信息
        User user = (User) request.getSession().getAttribute("user");
        if (user == null) {
            //没有登陆
            response.sendRedirect("/login.html");
            return;
        } else {
            //放行。。
            chain.doFilter(request, response);
        }
    }
    public void init(FilterConfig config) throws ServletException {
    }
}