[Java] JSP笔记 - Filter 过滤器

Posted 爱音乐的孩子是小白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java] JSP笔记 - Filter 过滤器相关的知识,希望对你有一定的参考价值。

一、什么是Web过滤器

Servlet API 很久以前就已成为企业应用开发的基石,而 Servlet 过滤器则是对 J2EE 家族的相对较新的补充。

Servlet 过滤器是可插入的 Web 组件,它允许我们实现 Web 应用程序中的预处理和后期处理逻辑。过滤器支持 servlet 和 JSP 页面的基本请求处理功能,比如日志记录、性能、安全、会话处理、XSLT 转换,等等。 过滤器最初是随 Java Servlet 2.3 规范发布的。

Servlet 过滤器是什么?
Servlet 过滤器是小型的 Web 组件,它们拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和服务器之间交换的数据。过滤器是通常封装了一些功能的 Web 组件,这些功能虽然很重要,但是对于处理客户机请求或发送响应来说不是决定性的。典型的例子包括记录关于请求和响应的数据、处理安全协议、管理会话属性, 等等。过滤器提供一种面向对象的模块化机制,用以将公共任务封装到可插入的组件中,这些组件通过一个配置文件来声明,并动态地处理。

Servlet 过滤器中结合了许多元素,从而使得过滤器成为独特、强大和模块化的 Web 组件。也就是说,Servlet 过滤器是:

声明式的:过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明。这样允许添加和删除过滤器,而无需改动任何应用程序代码或 JSP 页面。

动态的:过滤器在运行时由 Servlet 容器调用来拦截和处理请求和响应。

灵活的:过滤器在 Web 处理环境中的应用很广泛,涵盖诸如日志记录和安全等许多最公共的辅助任务。过滤器还是灵活的,因为它们可用于对来自客户机的直接调用执行预处理和后期处 理,以及处理在防火墙之后的 Web 组件之间调度的请求。最后,可以将过滤器链接起来以提供必需的功能。

模块化的:通过把应用程序处理逻辑封装到单个类文件中,过滤器从而定义了可容易地从请求/响应链中添加或删除的模块化单元。

可移植的:与 Java 平台的其他许多方面一样,Servlet 过滤器是跨平台和跨容器可移植的,从而进一步支持了 Servler 过滤器的模块化和可重用本质。

可重用的:归功于过滤器实现类的模块化设计,以及声明式的过滤器配置方式,过滤器可以容易地跨越不同的项目和应用程序使用。

透明的:在请求/响应链中包括过滤器,这种设计是为了补充(而不是以任何方式替代)servlet 或 JSP 页面提供的核心处理。因而,过滤器可以根据需要添加或删除,而不会破坏 servlet 或 JSP 页面。


所以 Servlet 过滤器是通过一个配置文件来灵活声明的模块化可重用组件。过滤器动态地处理传入的请求和传出的响应,并且无需修改应用程序代码就可以透明地添加或删除它们。最后,过滤器独立于任何平台或者 Servlet 容器,从而允许将它们容易地部署到任何相容的 J2EE 环境中。

 

二、第一个过滤器

package com.po;

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 FirstFilter implements Filter {
    
    @Override
    public void destroy() {
        System.out.println("destroy");
    }

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

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init: " + filterConfig.toString());
    }
}

过滤器需要实现 Filter 接口:

init():这个方法在容器实例化过滤器时被调用,它主要设计用于使过滤器为处理做准备。该方法接受一个 FilterConfig 类型的对象作为输入。

doFilter(): 与 servlet 拥有一个 service() 方法(这个方法又调用 doPost() 或者 doGet())来处理请求一样,过滤器拥有单个用于处理请求和响应的方法——doFilter()。这个方法接受三个输入参数:一个 ServletRequest、response 和一个 FilterChain 对象。

destroy():正如您想像的那样,这个方法执行任何清理操作,这些操作可能需要在自动垃圾收集之前进行。展示了一个非常简单的过滤器,它跟踪满足一个客户机的 Web 请求所花的大致时间。

 

三、配置 web.xml ,启用过滤器

先来看下这个图:

 

现在,我们在 web.xml 中,添加这些 xml 内容:

    <!-- 过滤器配置 -->
    <filter>
        <filter-name>FirstFilter</filter-name>
        <filter-class>com.po.FirstFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FirstFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

filter 标签用来声明一个过滤器,每个过滤器只能有一个filter标签。

filter-mapping 标签用来指定过滤器的作用对象。上面的示例中 url-pattern 设定为了 "/*", 代表对所有jsp请求有效。

更改为 web.xml 后,需要重启 tomcat 服务器。

在重启服务器时,我们可以在控制台日志中看到我们的过滤器 init() 被调用了。

在浏览器 中访问一个页面,doFilter 会被执行。

 

四、 常见问题

 

1. 过滤器是否能够改变用户请求的WEB资源呢? 也就是能否改变用户请求的路径?

回答: 能。比如检测到用户没有登录,可以请WEB资源指向到登录页面。

 

2. 过滤器能否直接返回数据,能不能直接处理用户请求?

回答: 不能。因为过滤器不是一样标准的 Servlet ,它不能直接返回数据。它要是么指向一个WEB资源,要么是重定向到其它的WEB资源。

 

五、多个过滤器

 

WEB服务程序是可以支持多个过滤器的。

假如一个URL和多个过滤器指定的URL相符时,会生成一个过滤器链。

服务器会按照web.xml中过滤器定义的先后顺序组装成过滤器链。

过滤器链的执行过程:

 

六、过滤器的分类

过滤器默认是 Request 类型。

FORWARD 过滤器:

    <filter>
        <filter-name>FirstFilter</filter-name>
        <filter-class>com.po.FirstFilter</filter-class>        
    </filter>
    <filter-mapping>
        <filter-name>FirstFilter</filter-name>
        <url-pattern>/index.jsp</url-pattern>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

INCLUDE 过滤器和 FORWARD 声明类似,将dispatcher改为INCLUDE就可以了。

错误过滤器:

    <!-- 错误过滤器 -->
    <error-page>
        <error-code>404</error-code>
        <location>/error.jsp</location>
    </error-page>
    
    <filter>
        <filter-name>ErrorFilter</filter-name>
        <filter-class>com.po.ErrorFilter</filter-class>        
    </filter>
    <filter-mapping>
        <filter-name>ErrorFilter</filter-name>
        <url-pattern>/error.jsp</url-pattern>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

在 Servlet 3.0 及之后的版本中,增加了异步支持。还增加使用注解的方式来定义过滤器(也就是说不需要我们修改 web.xml)了。

七、 @WebFilter 注解定义过滤器

我们先来看看 @WebFilter的常用属性。

示例: (错误过滤器)

package com.po;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

@WebFilter(
        filterName="ErrorFilter",
        value={"/error.jsp"},
        dispatcherTypes={javax.servlet.DispatcherType.ERROR})
public class ErrorFilter implements Filter {

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

}

上面的代码,使用注解来定义过滤器。

下面是异步过滤的示例:

首先是新建一个Servlet,这个Servlet用来处理复杂的异步事务。

package servlet;

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

@WebServlet(name="AsyncServlet",asyncSupported=true,urlPatterns={"/servlet/AsyncServlet"},description="异步过滤器示例Servlet")
public class AsyncServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("业务请求开始.");
        AsyncContext context = req.startAsync();
        // 开启异步线程
        new Thread(new Executor(context)).start();
     // req.getRequestDispatcher("/index.jsp").forward(req, resp);
        // resp.getWriter().close(); System.out.println(
"业务请求结束."); } public class Executor implements Runnable { private AsyncContext context; public Executor(AsyncContext context) { this.context = context; } @Override public void run() { try { // 复杂业务处理 System.out.println(context.getRequest().getScheme()); Thread.sleep(10 * 1000); System.out.println("业务执行完成."); } catch (InterruptedException e) { e.printStackTrace(); } } } }

接下来,建立一个异步过滤器。

package servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

@WebFilter(filterName="AsyncFilter",
    urlPatterns={"/servlet/AsyncServlet"},
    asyncSupported=true,
    dispatcherTypes={javax.servlet.DispatcherType.ASYNC}
)
public class AsyncFilter implements Filter {

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

}

异步过滤器的特性就是能很快的响应用户请求,复杂的业务处理过程放到新的线程中执行。

当然了,在我们上面的示例代码中, AsyncServlet.onGet里面只是启动一个业务处理线程,并没写响应的代码,所以会导致客户端出现一个 500 错误。实际业务中,输出

 

【感谢】

慕课网Fcming 讲师

 

以上是关于[Java] JSP笔记 - Filter 过滤器的主要内容,如果未能解决你的问题,请参考以下文章

Java过滤器Filter的原理及配置_学习笔记

java连个过滤器之间的跳转

filter过滤器配置如何不过滤一些页面

java过滤非法字符的filter

JSP中用filter 过滤某个包中的所有servlet

过滤器简介--学习笔记