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

336 阅读7分钟

过滤器、监听器

第一章 过滤器简介

1、Java Web的三大组件

组件作用实现接口
Servlet小应用程序,在JavaWeb的服务器中主要做为控制器来使用 可以处理用户的请求并且做出响应javax.servlet.Servlet
Filter过滤器,对用户发送的请求或响应进行集中处理,实现请求的拦截javax.servlet.Filter
Listener监听器,在某些框架中会使用到监听器,如:Spring框架 在Web执行过程中,引发一些事件,对相应事件进行处理。主要是监听Servlet的3大域对象的创建销毁以及属性的变化javax.servlet.XxxListener 每个事件有一个接口

2、过滤器是什么

过滤器引入.bmp

过滤器(filter)本质是Java中预先定义好了不同的接口,和servlet类似。可以过滤不同的内容,具体怎么过滤,需要使用者定义一个实现类,然后实现接口中的过滤方法,在方法中书写过滤的条件。filter是对客户端访问资源的过滤,符合条件放行,不符合条件不放行。

简而言之,是在servlet之前执行,拦截(过滤)请求,将servlet中一些共同的业务代码抽取出来,这就是过滤器作用。比如:请求乱码处理。

第二章 过滤器开发入门

1、过滤器API介绍

Filter是由Tomcat启动时创建,是javax.servlet包下面的一个接口,这个接口中定义了3个方法。

package javax.servlet;
import java.io.IOException;
public interface Filter {
    void init(FilterConfig var1) throws ServletException;
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    void destroy();
}
方法说明
void init(FilterConfig filterConfig)过滤器对象创建的时候调用的方法
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)执行过滤的方法
void destory()过滤器销毁的时候调用的方法

说明:

1.tomcat服务器一启动就会调用过滤器中的无参构造方法创建过滤器类的对象,然后立刻调用初始化init方法。

2.每次访问拦截的资源都会执行一次执行过滤的方法doFilter

3.正常关闭tomcat服务器,执行销毁过滤器的方法destory

2、过滤器开发步骤

项目中如果需要使用到过滤器,使用Servlet3.0的注解方式进行创建。创建步骤如下:

1533288412150.png

  • 第一步:新建一个普通java类,实现Filter接口;

  • 第二步:根据提示重写Filter接口的3个方法;

  • 第三步:在这个类上面通过注解的方式配置这个Filter;

    • 注解@WebFilter:配置Filter的注解,这个注解有两个参数;

      • filterName:设置过滤器的名称;
      • urlPatterns:配置这个过滤器要拦截的资源的路径;
  • 第四步:在doFilter()方法中,书写过滤的业务逻辑;

    • chain.doFilter()方法放行;

过滤器是在请求资源之前执行,这点非常重要

1533299852890.png

【参考代码】

filter的web.xml版本

import javax.servlet.*;
import java.io.IOException;
​
public class FilterOne implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //doFilter 每一次请求都会执行  进行过滤业务逻辑处理
        //如果想放行
//        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
    }
}

说明:filterChain.doFilter(servletRequest,servletResponse);方法被执行,相当于放行的功能。

XML配置 和servlet一样,需要在web.xml中进行配置

     <filter>
        <filter-name>one</filter-name>
        <filter-class>cn.ithea.filter01.FilterOne</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>one</filter-name>
        <url-pattern>/filter01.html</url-pattern>
    </filter-mapping>

filter01.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>来拦截我啊!</h1>
</body>
</html>

效果:访问http://localhost:8080/filter01.html

浏览器没有得到html资源。

1537235651993.png

Filter的注解版本

image.png

注解代码参考

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
​
@WebFilter("/filter02.html")
public class FilterTwo 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;
        //code...
        //放行
        System.out.println("执行过滤方法。。。");
        chain.doFilter(request, response);  //放行,没有拦截,准许用户访问资源
    }
​
    public void init(FilterConfig config) throws ServletException {
​
    }
​
}
​

filter02.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>请不要拦截我</h1>
</body>
</html>

效果:

访问链接:http://localhost:8080/filter02.html

1537235902680.png

说明:可以修改idea中的配置过滤器的模板:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
@javax.servlet.annotation.WebFilter("/${Entity_Name}")
public class ${Class_Name} implements javax.servlet.Filter {
    @Override
    public void destroy() {
    }
    @Override
    public void doFilter(javax.servlet.ServletRequest req, javax.servlet.ServletResponse resp, javax.servlet.FilterChain chain) throws javax.servlet.ServletException, java.io.IOException {
        javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest)req;
        javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse)resp;
        //your  code.... 
        chain.doFilter(request, response);
    }
    @Override
    public void init(javax.servlet.FilterConfig config) throws javax.servlet.ServletException {
    }
}

总结:

1)chain.doFilter(request, response) 方法的作用:

当不写chain.doFilter(request, response) 方法时候,html文件访问不到,被拦截;

当有了chain.doFilter(request, response) 方法时候,html文件可以访问,被放行;所以:chain.doFilter(request, response) 具有放行的作用。

3、过滤器应用练习

需求:实现横刀夺爱案例来应用过滤器

案例描述:新建表单,用input标签提交数据,如果表单提交数据中含有"love me",不让servlet接收。

案例步骤:

a:新建servlet,接收表单提交数据,含有"love me",就输出"我们结婚吧"。

b:新建filter,在doFilter()之前判断表单提交的数据是否含有"love me",有的话,拦截,并输出,"抢走新娘"

1)前端页面:

marry.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/marryServlet" method="post">
    女神:<input type="text" name="word"><br>
    <input type="submit" value="发短信">
</form>
</body>
</html>

2)后台servlet:

MarryServlet.java

@WebServlet("/marryServlet")
public class MarryServlet 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 {
        //解决响应乱码问题
        response.setContentType("text/html;charset=utf-8");
        String word = request.getParameter("word");
        if (word.contains("loveme")){
            response.getWriter().print("我们结婚吧");
        }
        System.out.println("我和女神聊天");
    }
}
​
​

3)过滤器:

MarryFilter.java

//这里路径书写拦截的servlet
@WebFilter("/marryServlet")
public class MarryFilter 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;
        String word = request.getParameter("word");
        if (word.contains("loveme")){
            //不放行
            System.out.println("抢走新娘!哈哈");
            return;
        }else {
            //放行
            chain.doFilter(request, response);
        }
    }
​
    public void init(FilterConfig config) throws ServletException {
​
    }
​
}
​

效果:

访问链接:http://localhost:8080/marry.html 输入带有loveme的信息,点击发短信按钮

1537236117348.png

过滤器拦截到,进行横刀夺爱了

1537236214121.png

第三章 过滤器的执行流程

1、过滤器中代码执行顺序

ProcessFilter.java

@WebFilter("/process")
public class ProcessFilter implements Filter {
    @Override
    public void destroy() {
    }
    @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);
        System.out.println("doFilter()之后执行。。。");
    }
    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}

ProcessServlet.java

@WebServlet("/process")
public class ProcessServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
​
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Servlet执行了。。。");
    }
}
​

执行效果图:

image.png

运行原理图

1533297376663.png

说明:servlet响应给浏览器的内容,过滤器也对其进行了拦截过滤。

2、过滤器中请求和响应对象

过滤器的doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)方法中的request对象和response对象与servlet中请求对象和响应对象相同。

过滤器的执行流程如下:

  1. 客户端(浏览器)的HttpRequest到达Servlet之前,被Filter的doFilter()方法拦截;
  2. 根据需要,我们可以获取或者修改HttpRequest对象中的数据;
  3. 在这个请求响应给浏览器之前,Filter拦截了HttpResponse对象;
  4. 根据需要,我们可以获取或者修改HttpReponse对象中的数据;
  5. 最后,由doFilter中的chain决定是否让这个请求通过;

代码:ProcessServlet.java

@WebServlet("/processServlet")
public class ProcessServlet 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(request);
        System.out.println(response);
        System.out.println("servlet被执行了。。。");
    }
}

ProcessFilter.java

@WebFilter("/processServlet")
public class ProcessFilter 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;
        //code...
        System.out.println("在doFilter()之前执行。。。");
        System.out.println(request);
        System.out.println(response);
        chain.doFilter(request, response);
        System.out.println("在doFilter()之后执行。。。");
        // 拦截请求对象和响应对象就是servlet请求对象和响应对象
    }
​
    public void init(FilterConfig config) throws ServletException {
​
    }
​
}
​

控制台运行效果图

image.png

3、多个Filter执行顺序

问题:如果多个过滤器都对相同路径进行匹配,执行顺序该是什么?

Filter默认是按照字母顺序执行的,如果过滤器名字第一个字母相同,再看过滤器名字的第二个字母,以此类推。从而形成一个执行链条。

代码AFilter.java

@WebFilter("/apple")
public class AFilter implements Filter {
    @Override
    public void destroy() {
    }
    @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("这是在AFilter之前执行。。。");
        chain.doFilter(request, response);
        System.out.println("这是在AFilter之后执行。。。");
    }
    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}
​

代码:BFilter.java

@WebFilter("/apple")
public class BFilter implements Filter {
    @Override
    public void destroy() {
    }
    @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("这是在BFilter之前执行。。。");
        chain.doFilter(request, response);
        System.out.println("这是在BFilter之后执行。。。");
    }
    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}

代码:CFilter.java

@WebFilter("/apple")
public class CFilter implements Filter {
    @Override
    public void destroy() {
    }
    @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("这是在CFilter之前执行。。。");
        chain.doFilter(request, response);
        System.out.println("这是在CFilter之后执行。。。");
    }
    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}

代码:AppleServlet.java

@WebServlet("/apple")
public class AppleServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("AppleServlet");
    }
}

上述代码执行效果图:

image.png

执行流程图:

1533299334920.png