servlet过滤器的分类
JavaWeb过滤器分为四类,分别对应四种触发类型: REQUEST、FORWARD、INCLUDE、ERROR。若在注册的时候没有特别声明,则默认为REQUEST

REQUEST
比较常用的类型,不指定dispatcherType则默认为Request。很明显,该类过滤器由request动作触发。下面简单写一个demo: 新建若干个jsp文件和一个Filter,Filter1.java示例代码如下(为方便观察,只保留了dofilter方法):
package org.devsong.filter;

import java.io.IOException;

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

@WebFilter(dispatcherTypes = { DispatcherType.REQUEST }, description = "normal filter : request", urlPatterns = {
        "/index.jsp" })
public class Filter1 implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest res = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        System.out.println("Filter1: start doFilter, dispatcherType: request");
        res.getRequestDispatcher("/jsp/welcome.jsp").forward(res, resp);     //请求转发
        System.out.println("Filter1: end doFilter, dispatcherType: request");
    }

}
可以看到,此处将index.jsp设置为过滤对象,在doFilter方法体内请求转发至welcome.jsp。启动tomcat并访问index.jsp,可以看到控制台输出了doFilter方法体内的输出语句对应的内容,并且页面显示为welcome的内容。可见Filter1成功对index.jsp的访问请求进行了过滤。

FORWARD
此类型自然是对forward动作指令进行过滤,并且无论是jsp页面内的Forward标签还是Java代码中的forward指令都会被过滤处理。新建一个Filter,存为Filter2.Java,示例代码如下(为节省篇幅,未贴出import导包语句):
@WebFilter(dispatcherTypes = {DispatcherType.FORWARD}, urlPatterns = {"/*"})
public class Filter2 implements Filter {

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
            throws IOException, ServletException {
        HttpServletRequest res = (HttpServletRequest) arg0;
        HttpServletResponse resp = (HttpServletResponse) arg1;
        
        System.out.println("Filter2: start doFilter  dispatcherType: forward");
        resp.sendRedirect(res.getContextPath()+"/jsp/hello.jsp");          //重定向至hello.jsp
        System.out.println("Filter2: end doFilter  dispatcherType: forward");
    }

}
由于Filter1中已经有了一个forward请求转发指令,所以此处直接访问index.jsp即可,访问后可以看到Filter1和Filter2均进行了过滤操作,最终页面显示的是hello.jsp的内容。并且控制台输出了以下语句:
Filter1: start doFilter, dispatcherType: request
Filter2: start doFilter  dispatcherType: forward
Filter2: end doFilter  dispatcherType: forward
Filter1: end doFilter, dispatcherType: request
之所以说jsp页面内的forward标签也是同一样的效果。可以将Filter1中的请求转发语句注释掉,然后在index.jsp中加入一条语句:,然后访问index.jsp页面,可以发现效果是一致的。

INCLUDE
此类型是对指定页面的include进行过滤,用法与Forward类型一致。只是需要注意在jsp中的include包含指令不能采用引入其他资源文件等类似的方式,即以下写法是错误的,这样写看似没什么问题,其实路径是错的,由于是动态包含,page是作为一个函数参数处理,其得出的路径不是文档目录,有兴趣可以试一下看异常里面的提示内容,关于include路径问题,可以参看这篇文章:jsp中 中使用绝对路径的问题
<jsp:include page="<%=request.getContentPath %>/jsp/welcome.jsp" flush="true"/> //error

ERROR
此类是对错误访问的过滤,比较常见的错误为404、500等,可以先在web.xml中注册一下错误页面,错误页面的注册可以使用错误对应编码(如404等),也可以使用类的完全限定名,如javax.ServletException等,可以是标准异常类型,也可以是自己定义的异常。 此处只是简单配置了一个404异常处理页,注册代码如下:
<error-page>
    <error-code>404</error-code>
    <location>/jsp/error.jsp</location>
</error-page>

接着是Filter的设计,新建Filter3,其doFilter方法内只是简单打印输出,示例代码如下:
@WebFilter(dispatcherTypes = { DispatcherType.ERROR }, urlPatterns = { "/*" })
public class Filter3 implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("filter3: start doFilter  dispatcherType: error");
        System.out.println("an error detected!");
        chain.doFilter(request, response);
        System.out.println("filter3: end doFilter  dispatcherType: error");
    }

}
项目部署成功后,打开浏览器访问一个当前目录下不存在的页面,可以看到浏览器跳转到了自定义的404错误处理页面,并且eclipse控制台输出了以下信息:
filter3: start doFilter  dispatcherType: error
an error detected!
filter3: end doFilter  dispatcherType: error
值得注意的一点是用Eclipse内部的浏览器组件和IE浏览器有很大几率不能显示自定义错误处理页面,如需显示需要做相应的设置。教程:web服务器中error-page配置无效的解决方法

总结
可以注意到一点,从servlet3.0开始,可以用注解注册相关组件,很方便,并且,3.0开始还引入了一个异步处理特性,允许servlet将耗时的操作用异步处理的方式处理,让出有限的容器资源供其它请求使用,等耗时的业务处理完毕再提交处理结果对客户端进行响应。 此处涉及到的要点有四个:
(1)servlet要进行异步操作则servlet之前的filter也需要支持异步操作,异步支持通过asyncSupported来标识。
(2)获取异步处理上下文:AsyncContext,通过request.startAsync()方法获取。
(3)业务处理完成后要使用AsyncContext的complete()方法告知servlet业务处理完毕,以进行后续的响应。
(4)异步处理的时间不能长于浏览器允许等待的最大时长,否则浏览器会认为网络通信失败,这个时长不同浏览器有不同的设定,chrome一般为30秒。


下面依然用一个简单的例子来演示:
新建Filter4,示例如下:
@WebFilter(dispatcherTypes = { DispatcherType.REQUEST }, asyncSupported = true, urlPatterns = { "/servlet" })
public class Filter4 implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("filter4: start doFilter        asyncSupported");
        chain.doFilter(request, response);
        System.out.println("filter4: end doFilter        asyncSupported");
    }
}
配置过滤对象为“/servlet”斌且添加异步处理支持。 新建TestServlet.java,示例代码如下:
package org.devsong.service;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

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;

/**
 * Servlet implementation class TestServlet
 */
@WebServlet(name = "TestServlet", asyncSupported = true, urlPatterns = { "/servlet" })
public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private PrintWriter out;

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        out = response.getWriter();

        out.println("servlet start...");
        AsyncContext context = request.startAsync();    //获取异步处理上下文
        new MyThread(context).start();
        out.println("servlet stop...");
    }

    //异步处理线程类
    private class MyThread extends Thread {
        // 可以从获取的AsyncContext中获取request和response对象
        private AsyncContext context;

        public MyThread(AsyncContext context) {
            this.context = context;
        }

        @Override
        public void run() {
            super.run();

            try {

                Thread.sleep(1000 * 10);
                System.out.println("Business operation completed...");

                out.println("Business operation completed... " + new Date());
                //通知Servlet异步处理已完成,可以提交请求响应。 这一步很必要!!!
                context.complete(); 

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
tomcat部署项目完毕后,在浏览器访问TestServlet类,然后可以在控制台立即看到filter输出的内容,十秒之后,可以在浏览器看到以下类似内容:
servlet start...
servlet stop...
Business operation completed... Sat Jul 22 16:21:07 CST 2017

对于Filter的类型选择,直接在eclipse新建Filter的时候可以看到相关提示,可以简单勾选而不用手动编写,如下:
文章正文图片

而对于Async的支持同样可以勾选:
文章正文图片
若您发现文章存在问题或者您有不同的见解,欢迎留言或者给我邮件,我会及时学习并更正。
It's
欢迎访问本站,欢迎留言、分享、点赞。愿您阅读愉快!
*转载请注明出处,严禁非法转载。
https://www.devsong.org
QQ留言 邮箱留言
头像
引用:
取消回复
提交
涂鸦
涂鸦
热门