板块一 Servlet编程:第九节 过滤器全解 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程:第九节 过滤器全解

程序员始终在为代码的可复用性努力着,过滤器也是一个典型的提高可复用性的工具,它让我们在一些逻辑上可以实现一劳永逸的效果

一、什么是过滤器

Filter 即为过滤 ,用于在Servlet之外对Request或者Response进行修改。它主要用于对用户请求进行预处理 ,也可以对HttpServletResponse进行后处理

使用Filter的完整流程:Filter 对用户请求进行预处理,接着将请求交给Servlet 进行处理并生成响应,最后Filter再对服务器响应进行后处理。 举个栗子,在 板块一 Servlet编程:第三节Request 中,请求响应进入Servlet的时候,每个文件都要对请求响应作中文乱码的处理,非常难受,如果在数据流入每个Servlet文件前面都经过一个过滤器,就能在过滤器里一劳永逸解决中文乱码问题了

在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链

请求时 先配置先执行;响应时 以相反的顺序执行。

在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。

在HttpServletResponse到达客户端之前,拦截HttpServletResponse。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

二、过滤器的实现

在当前包下新建一个Fileter目录,作过滤器层

在Filter目录下新建filter1.java,现在的目录结构为

一个过滤器拦截一个目录

在filter1.java中实现一个过滤器需要以下步骤

实例

  • 实现接口
  • 重写实现生命周期的三个方法

    其中init()destroy()显然是我们耳熟能详的过滤器的生命周期的两个方法。而doFilter()就是过滤器的核心:过滤方法
    重写方法后,要在其中添加filterChain.doFilter(servletRequest,servletResponse);表示过滤后能放行的资源,也就是放行了来时的请求以及去时的响应,否则请求响应无法到达资源
    因此总体的doFliter()方法代码为
java 复制代码
	@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("正在经过过滤器");
        filterChain.doFilter(servletRequest,servletResponse);
    }
  • 最后加上过滤器的访问注解@WebFilter("/start")
    注解中的目录就是过滤器将会过滤的目录,当然通常情况下是很多个项目一起过滤,此处只过滤start.java

最终filter.java的代码为

java 复制代码
package www.caijiyuan.Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;

import java.io.IOException;

@WebFilter("/start")
public class filter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器初始化成功");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("正在经过过滤器");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁");
    }
}

它将过滤的start.java文件内容为

java 复制代码
package www.caijiyuan.Servlt;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet("/start")
public class start extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("访问到Start");
    }
}

启动服务器,在浏览器中访问start,即可得到

可以看出服务器访问start.java文件之前是先经过lfilter1.java过滤器的

多个过滤器(Filter链)拦截一个目录

就如一、什么是过滤器 中图示的,当有多个过滤器时,对于同一个文件请求的拦截顺序和响应的拦截顺序刚好相反,我们用实例来验证

在过滤器层中新添一个filter2

除了文件名内容与filter1完全一致

java 复制代码
package www.caijiyuan.Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;

import java.io.IOException;

@WebFilter("/start")
public class filter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器初始化成功");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter2正在拦截");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("filter2处理响应");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁");
    }
}

此时再启动服务器,在浏览器中访问start.java即可在控制台打印

这就是filter链的演示效果,此外,如果filter1和filter2二者有一没有对请求响应进行filterChain.doFilter放行就会导致最终无法到达资源

三、过滤器处理中文乱码问题

早在Servlet的request和response两大对象小节我们就已经总结过中文乱码问题,现在我们就假设Tomcat8及以上POST请求时,使用Fliter处理中文乱码问题

实例:通过EncoderFliter.java过滤hello.jsp向after.java传递的中文参数

  • 实现生命周期:新建一个EncoderFliter.java的过滤器,implements自父类是Servlet包的Filter,并且在类中重写三个生命周期方法init\doFilter\destroy注意
  • 请求响应类型转换:doFilter中的request和response对象来自Servlet的实体,而我们之前用的request和response是来自HttpServlet的实体,因此需要在doFilter方法中类型转换
  • 处理乱码request.setCharacterEncoding("UTF-8");
  • 放行资源filterChain.doFilter(request,response);
  • Filter注释标识过滤范围,"*"为全部

最终EncoderFliter.java代码为

java 复制代码
package www.caijiyuan.Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*")
public class EncodeFliter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 基于HTTP
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 处理POST请求乱码
        request.setCharacterEncoding("UTF-8");
        //放行资源
        filterChain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}

此时的项目结构为

在hello.jsp中写一个POST表单用于传递请求参数

java 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hello</title>
    <h1>Tomcat with IDEA</h1>
</head>
<body>
<form action="after" method="post">
    输入参数<input type="text" name="name">
    <button>提交</button>
</form>
</body>
</html>

after.java用于接收参数

java 复制代码
package www.caijiyuan.Servlt;


import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet("/after")
public class after extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        System.out.println(name);
    }
}

启动服务器在浏览器中访问hello.jsp,并传中文参

在没有使用过滤器之前,after.java在控制台打印

使用了过滤器之后,after.java在控制台打印

说明参数在被after接受之前确实先经过EncoderFliter.java过滤器,并且成功执行了doFilter里的设置。

这在我们有大量文件需要处理相同的设置时 尤为方便,可以达到一劳永逸的效果,例如在访问一个购物网站时有些资源是登录以后才能查看的,例如在未登录状态下访问购物车就是一个非法访问,应该跳转到登录界面。这个非法访问拦截操作就可以用过滤器来实现

四、使用XML配置过滤器

除了用Servlet配置一个过滤器,其实我们还可以使用web.xml帮我们配置过滤器一些特定的参数,其基本格式为

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <filter>
        <filter-name>FilterName</filter-name>
        <filter-class>包的路径</filter-class>
        <!-- Optional: Filter initialization parameters -->
        <init-param>
            <param-name>参数名</param-name>
            <param-value>参数值</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>FilterName</filter-name>
        <url-pattern>/urlPattern</url-pattern>
        <!-- Optional: Specify dispatcher types -->
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

</web-app>

例如,我们上面的处理中文乱码的过滤器就可以这样写

EncodeFilter.java

java 复制代码
package www.caijiyuan.Filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class EncodeFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        // 基于HTTP
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 处理POST请求乱码
        request.setCharacterEncoding("UTF-8");
        //放行资源
        filterChain.doFilter(request,response);
    }
}

可以看出少了注解,此时过滤器覆盖的路径可以在web.xml中配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <filter>
        <filter-name>EncodeFilter</filter-name>
        <filter-class>www.caijiyuan.Filter.EncodeFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>EncodeFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

此时实现的效果与使用Servlet配置过滤器一致

注意,XML的配置必须写在中,若配置XML文件请保证语法配置正确 ,否则Tomcat无法正常启动

可以看到XML中过滤器还有初始参数这一项

xml 复制代码
<init-param>
    <param-name>参数名</param-name>
    <param-value>参数值</param-value>
</init-param>

举个栗子可以这样使用:过滤器中使用初始化参数

xml 复制代码
<filter>
    <filter-name>CustomFilter</filter-name>
    <filter-class>com.example.CustomFilter</filter-class>
    <init-param>
        <param-name>apiKey</param-name>
        <param-value>YOUR_API_KEY</param-value>
    </init-param>
</filter>

我们定义了一个名为CustomFilter的过滤器,并为它指定了一个名为apiKey的初始化参数,其值为YOUR_API_KEY

在过滤器的init()方法中,可以通过FilterConfig对象来获取这个初始化参数

java 复制代码
public class CustomFilter implements Filter {
    private String apiKey;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        apiKey = filterConfig.getInitParameter("apiKey");
    }
    ...
}

如此或取了初始化参数的值,并将其存储在apiKey变量中供过滤器的其他方法使用,初始化参数是可选的,可以根据需要在过滤器的配置中添加或删除它们

以上就是此小节的所用内容了,我们学习了过滤器最基础的使用方法,下一节中我们将学习过滤器的兄弟:监听器

相关推荐
独孤求败Ace11 小时前
第42天:Web开发-JavaEE应用&Servlet技术&路由配置&生命周期&过滤器Filter&监听器Listen
前端·servlet·java-ee
逸狼11 小时前
【JavaEE进阶】Spring MVC(2)
spring·java-ee·mvc
火烧屁屁啦15 小时前
【JavaEE进阶】MyBatis通过XML实现增删改查
xml·java-ee·mybatis
zhyhgx16 小时前
【Spring】详解Spring IOC&DI
java·spring boot·后端·spring·java-ee·intellij-idea
小突突突16 小时前
总结单例模式的写法(在线程安全的情况下)
java·开发语言·后端·单例模式·java-ee
Beekeeper&&P...1 天前
Spring Security,servlet filter,和白名单之间的关系
hive·spring·servlet
百香果果ccc1 天前
Servlet中HttpServletRequest和HttpServletResponse的常用API
servlet
计算机学姐1 天前
基于SpringBoot的鲜花商城
java·vue.js·spring boot·后端·mysql·spring·java-ee
等待的L先生1 天前
HttpServlet详解
http·javaee·interceptor·filter·event·httpservlet
2025年一定要上岸2 天前
JAVA EE初阶 - 预备知识(四)
java·java-ee