Java Servlet学习笔记——9. FIlters

写在前面

这一部分来学习Servlet中的Filters,Filter是拦截Request请求的对象: 在用户的请求访问资源前处理ServletRequest以及ServletResponse, 它可用于日志记录、 加解密、 Session检查、 图像文件保护等。 通过Filter可以拦截处理某个资源或者某些资源。Filter的配置可以通过Annotation或者部署描述来完成。当一个资源或者某些资源需要被多个Filter所使用到,且它的触发顺序很重要时, 只能通过部署描述来配置。

Filter API

Filter相关的接口, 包含Filter、 FilterConfg、 FilterChain。

Filter的实现必须继承javax.servlet.Filter接口。 这个接口包含了Filter的3个生命周期: init、 doFilter、destroy。

Servlet容器初始化Filter时, 会触发Filter的init方法, 一般来说是在应用开始时。 也就是说, init方法并不是在该Filter相关的资源使用到时才初始化的, 而且这个方法只调用一次, 用于初始化Filter。 init方法的定义如下:

void init(FilterConfig filterConfig)

当Servlet容器每次处理Filter相关的资源时, 都会调用该Filter实例的doFilter方法。 Filter的doFilter方法包含ServletRequest、 ServletResponse、 FilterChain这3个参数。doFilter的定义如下:

void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

doFilter的实现中访问ServletRequet、 ServletResponse。 这也就意味着允许给ServletRequest增加属性或者增加Header。 当然也可以修饰ServletRequest或者ServletRespone来改变它们的行为。

在Filter的doFilter的实现中, 最后一行需要调用FilterChain中的doChain方法。 注意Filter的doFilter方法里的第3个参数, 就是filterChain的实例:

filterChain.doFilter(request, response)

一个资源可能需要被多个Filter关联到(更专业一点来说, 这应该叫作Filter链条) , 这时Filter.doFilter()的方法将触发Filter链条中下一个Filter。 只有在Filter链条中最后一个Filter里调用的FilterChain.doFilter(), 才会触发处理资源的方法。

在Filter.doFilter()的实现中, 没有在结尾处调用FilterChain.doFilter()的方法, 那么该Request请求中止, 后面的处理就会中断。

Filter接口中, 最后一个方法是destroy, 它的定义如下:

void destroy()

该方法在Servlet容器要销毁Filter时触发, 一般在应用停止的时候进行调用。

除非Filter在部署描述中被多次定义到, 否则Servlet窗口只会为每个Filter创建单一实例。 由于Serlvet/JSP的应用通常要处理用户并发请求, 此时Filter实例需要同时被多个线程所关联到, 因此需要非常小心地处理多线程问题。

Filter配置

当完成Filter的实现后, 就可以开始配置Filter了。Filter的配置需要如下步骤:

  • 确认哪些资源需要使用这个Filter拦截处理。
  • 配置Filter的初始化参数值, 这些参数可以在Filter的init方法中读取到;
  • 给Filter取一个名称。 一般来说, 这个名称没有什么特别的含义, 但在一些特殊的情况下, 这个名字十分有用。 例如, 要记录Filter的初始化时间, 但这个应用中有许多的Filter, 这时它就可以用来识别Filter了。

FilterConfig接口允许通过它的getServletContext的方法来访问ServletContext:

ServletContext getServletContext()

如果配置了Filter的名字, 在FilterConfig的getFilterName中就可以获取Filter的名字。 getFilterName的定义如下:

java.lang.String getFilterName()

最重要的还是要获取到开发者或者运维给Filter配置的初始化参数。 为了获取这些初始化参数,需要用到FilterConfig中的两个方法, 第一个方法是getParameterNames:

java.util.Enumeration<java.lang.String> getInitParameterNames()

这个方法返回Filter参数名字的Enumeration对象。如果没有给这个Filter配置任何参数, 该方法返回的是空的Enumeration对象。第二个方法是getParameter:

java.lang.String getInitParameter(java.lang.String parameterName)

有两种方法可以配置Filter: 一种是通过WebFilter的Annotation来配置Filter, 另一种是通过部署描述来注册。 使用@WebFilter的方法, 只需要在Filter的实现类中增加一个注解即可, 不需要重复地配置部署描述。 当然, 此时要修改配置参数, 就需要重新构建Filter实现类了。 换句话说, 使用部署描述意味着修改Filter配置只要修改一下文本文件就可以了。

使用@WebFilter, 你需要熟悉下表中所列出来的参数, 这些参数是在WebFilter的Annotation里定义的。所有参数都是可选的。

属性 描述
asyncSupported Filter是否支持异步操作
description Filter的描述
dispatcerTypes Filter所生效范围
displayName Filter的显示名
filterName Filter的名称
initParams Filter的初始化参数
largeIcon Filter的大图名称
servletName Filter所生效的Servlet名称
smallIcon Filter的小图名称
urlPatterns Filter所生效的URL路径
value Filter所生效的URL路径

下述@WebFilter标注配置了一个Filter, 该名称为DataCompressionFilter, 且适用于所有资源:

@WebFilter(filterName="DataCompressionFilter", urlPatterns={"/*"})

如果使用部署描述中的filter、 filter-mapping元素定义, 那么它的内容如下:

<filter>
    <filter-name>DataCompressionFilter</filter-name>
    <filter-class>
        the fully-qualified name of the filter class
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>DataCompresionFilter</filter-name>
    <url-pattern>/ *</url-pattern>
</filter-mapping>

下述的Filter配置, 描述了两个初始化参数:

@WebFilter(filterName = "Security Filter", urlPatterns = { "/ *" },
    initParams = {
                @WebInitParam(name = "frequency", value = "1909"), 
                @WebInitParam(name = "resolution", value = "1024")
            })

如果使用部署描述中的filter、 filter-mapping元素,那么该配置应该为:

<filter>
    <filter-name>Security Filter</filter-name>
    <filter-class>filterClass</filter-class>
    <init-param>
        <param-name>frequency</param-name>
        <param-value>1909</param-value>
    </init-param>
        <init-param><param-name>resolution</param-name>
        <param-value>1024</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>DataCompresionFilter</filter-name>
    <url-pattern>/ *</url-pattern>
</filter-mapping>

Filter顺序

如果多个Filter应用于同一个资源, Filter的触发顺序将变得非常重要, 这时就需要使用部署描述来管理Filter: 指定哪个Filter先被触发。 例如: Filter 1需要在Filter 2前被触发, 那么在部署描述中, Filter 1需要配置在Filter 2之前,通过部署描述之外的配置来指定Filter触发的顺序是不可能的。代码如下:

<filter>
    <filter-name>Filter1</filter-name>
    <filter-class>
        the fully-qualified name of the filter class
    </filter-class>
</filter>
<filter>
    <filter-name>Filter2</filter-name>
    <filter-class>
        the fully-qualified name of the filter class
    </filter-class>
</filter>

猜你喜欢

转载自blog.csdn.net/zy2317878/article/details/80637568