
本文共 6489 字,大约阅读时间需要 21 分钟。
文章目录
过滤器是J2EE Servlet的组件之一,用于对浏览器的请求与响应进行拦截处理,通常用于应用程序层面进行全局处理。当浏览器发出请求后,服务器中的过滤器会依次对请求进行拦截处理,处理结束后会发送给Servlet进行逻辑处理,待Servlet发出响应后,再依次经过过滤器返回给页面,这一整个过程被称为过滤链。
过滤器
- 所有过滤器都要实现javax.servlet.Filter接口
- 所有过滤器都要实现接口中的doFilter()方法
- 所有过滤器都要在web.xml中定义拦截范围
Filter接口的实现方法:
- 初始化方法-init();
- 拦截器实现方法-doFilter(),doFilter()方法中使用三个参数,请求-servletRequest,响应-servletResponse和过滤链-filterChain;
- 销毁方法-destroy()。
在doFilter()方法中实现了拦截逻辑后使用FilterChain的doFilter()方法继续向后续的处理逻辑发送请求或响应。
package com.filter;import javax.servlet.*;import java.io.IOException;public class FilterTest implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter拦截器成功!"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { }}
配置web.xml文件
filter
与servlet配置类似,先配置<filter>
包含两个子标签<filter-name>
和<filter-class>
:
<filter-name>
:过滤器的别名<filter-class>
:过滤器类的路径
init-param
在过滤器中可以在web.xml中的进行参数的初始化,可以更加灵活的调用或修改一下参数。在web.xml中的<filter>
标签中增加子标签<init-param>
,该标签下有两个子标签<param-name>
描述参数名称和<param-value>
描述参数值。若存在多个参数可重写<init-param>
标签定义新的参数。
init()方法中的参数FilterConfig用于获取web.xml中的参数,可以调用FilterConfig的getInitParameter(String s)
方法获取参数值。
filter-mapping
<filter-name>
:过滤器的别名,与<filter>
中的<filter-name>
对应<url-pattern>
:需要进行拦截的url地址
<?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>filter</filter-name> <filter-class>com.filter.FilterTest</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>
过滤器的生命周期
- 初始化-init()。在服务器启动时,会执行过滤器中的init()方法,初始化过滤器
- 执行过滤器-doFilter(),即后续的Servlet服务
- 当服务停止或重启时,将会执行销毁方法-destroy(),释放资源
过滤器的特性
- 过滤器的对象在Web服务启动后创建且只存在唯一对象
- 在并发环境中,过滤器使用多线程提供服务
关于ServletRequest接口
在J2EE中所有请求都是ServletRequest接口的子类,例如HttpServletRequest集成在了servlet-api.jar中。无论是ServletRequest或HttpServletRequest都是Oracle所定义的接口,没有实现类,所有的第三方厂商都需要去自己根据请求接口实现具体方法,例如Tomcat中就是由catalina.jar中的RequestFacade类中实现。响应类似。
所以在过滤器的实现方法中要先把ServletRequest/ServletResponse强制类型转换为HttpServletRequest/HttpServletResponse
注解
与Servlet相似,在Servlet 3.0之后提供了注解,用于简化web.xml的配置。在过滤器类的上一行书写@WebFilter(filterName = "filter", urlPatterns = "/*")
,filterName为过滤器的名称,urlPatterns为过滤器需要拦截的URL。
对于上文提到的参数初始化,也可以在注解中添加。使用属性initParams
来设置参数,值用{...}
来存储参数,在{...}
中使用注解@WebinitParam(name = "", value = "")
存放参数,name
为参数名称,value
为参数值,若存在多个参数可多次书写@WebinitParam(name = "", value = "")
来描述参数。即:
@WebFilter( filterName = "filter", urlPatterns = "/*", initParams = { @WebInitParam(name = "n1", value = "v1"), @WebInitParam(name = "n2", value = "v2") })
一般来说对于中大型的项目中的需要进行全局拦截,写在web.xml中,这样易于维护在需要修改某些业务时只修改配置文件即可不需要再去重新编译代码。对于小项目为了代码的便捷可以使用注解来配置Filter。
示例:
利用过滤器实现编码格式的转换
package com.filter;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class CharsetFilter implements Filter { private String encoding; @Override public void init(FilterConfig filterConfig) throws ServletException { encoding = filterConfig.getInitParameter("encoding"); System.out.println("encoding:" + encoding); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; req.setCharacterEncoding(encoding); resp.setContentType("text/html;charset=" + encoding); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { }}
<?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>charsetFilter</filter-name> <filter-class>com.filter.CharsetFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>charsetFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>
由此在servlet类中,就不再需要设置编码方式也可以正常显示中文。
url-pattern的过滤规则
地址的拦截规则主要有三种写法:
- 精准匹配地址:使用确切的地址告诉过滤器去对该地址进行过滤,例如:
/index.jsp
- 以前缀进行模糊匹配:在开始部分书写要过滤的地址名后用*来代替,可以直接过滤以该地址开头的链接,例如:
/servlet/*
- 以后缀进行模糊匹配:用*连接后缀名可以过滤符合后缀名的页面,例如
/*.jsp
此时需要注意以下/
的用法
/
指映射Web应用的根路径,只对Servlet生效。- 默认首页index.jsp会使
/
失效,因为在servlet中默认首页的优先级要高于映射中的地址,所以对于默认首页一般采取第一种精准匹配的方式指定对首页进行拦截。 - 区分
/
与/*
的区别,/
是指定根目录下文件不包括其他子目录,而/*
则指目录下的所有文件。
过滤链
一般来说每个过滤器都应具备一个单一的功能,所以在请求及响应过程中需要将多个过滤器串联起来,由此组成了过滤链。而过滤器的执行顺序则依据web.xml中的filter-mapping
的书写顺序依次执行,由doFilter()
方法中的FilterChain.doFilter()
方法进行传递。对于使用注解的方法配置过滤器时会按照过滤器类名进行降序排序执行,所以一般不建议使用注解的方法定义过滤链。
其中需要注意的是,第一过滤链的传递都是依靠FilterChain.doFilter()
方法,如果在某一环节不去书写该方法则会直接停止在该过滤器上不会再向下执行。第二FilterChain.doFilter()
方法一般写在最后,如果将其放在前面的话,会直接传递向后续的过滤器或servlet类,而不会执行方法后的内容,在经过servlet类之后发送响应时,才回去执行方法后的内容。
由此也可以看出过滤器不仅会在浏览器发送请求时对其进行过滤,在Servlet类执行后发起响应后也会依次经过过滤器。
示例:
创建三个过滤器
package com.filter.filterchain;import javax.servlet.*;import java.io.IOException;public class FilterChainA implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("This is Filter A"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { }}
其他两个结构相同类名改为FilterChainB和FilterChainC,并在web.xml中依次添加映射。启动Tomcat访问Servlet时就会依次打印结果。
按前文提过的将FilterChain.doFilter()
方法写在前边,则执行结果如下:
由此可以看出过滤链是双向的,不仅请求会通过过滤器,响应时也会通过过滤器。
发表评论
最新留言
关于作者
