一、Filter简介
Filter和Servlet一样默认都是单例的,Filter是请求到Servlet过程中的一个屏障,可以拦截和放行请求,像登录和注册这种请求若被拦截的话就无法登录和注册,也就无法执行一些需要登录后才能执行的操作了。
Filter是JavaWeb中的三大组件(Servlet、Filter、Listener)之一,Filter直译过来叫过滤器,是用来过滤请求的,Filter虽不能过滤响应,但可以改变响应。
Filter在JavaEE中实际上就是一个接口,我们可以实现这个接口达到一些目的。Filter的作用:
1️⃣Filter可以在请求到达目标资源(JSP、Servlet、Html、CSS)之前拦截请求,并作相应处理
2️⃣Filter可以放行请求,使请求到达目标资源
3️⃣Filter可以在响应到达浏览器之前做一些处理
二、Filter的应用
1、Filter的HelloWorld
①创建一个类并实现Filter接口
/**
* HttpFilter 为Filter提供一个模板,其他Filter只需要继承HttpFilter即可
*
* @author lenovo
*
*/
public abstract class HttpFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.init();
}
/**
* 该方法的目的是供子类重写,该方法会在服务器调用init(FilterConfig
* filterConfig)时被调用,以对Filter进行初始化操作
*
* @throws ServletException
*/
public void init() throws ServletException {
}
/**
* 该方法定义为抽象方法,因为我们大多处理的是Http的请求,
* 因此将入参改为了HttpServletRequest和HttpServletResponse
*
* @param req
* @param resp
* @param arg2
* @throws IOException
* @throws ServletException
*/
public abstract void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)
throws IOException, ServletException;
/**
* 服务器调用此方法时实现上会调用重写的doFilter(HttpServletRequest req, HttpServletResponse
* resp, FilterChain chain)
*/
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
this.doFilter(request, response, chain);
}
/**
* 通过该方法获取到filterConfig,继而可以获取到ServletContext,然后就可以获取配置文件中的参数
*
* @return
*/
public FilterConfig getFilterConfig() {
return filterConfig;
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
②在web.xml文件中注册Filter:一个filter-mapping中可以配置多个url-pattern
<filter>
<filter-name>别名</filter-name>
<filter-class>全类名</filter-class>
</filter>
<filter-mapping>
<filter-name>别名</filter-name>
<url-pattern>要拦截的地址</url-pattern>
</filter-mapping>
示例:这种一个filter-mapping中配置多个url-pattern的方式不推荐,可以使用多个filter-mapping进行配置,见下例
<filter>
<filter-name>HelloFilter</filter-name>
<filter-class>com.bdm.filters.HelloFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HelloFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
<url-pattern>/index.html</url-pattern>
</filter-mapping>
示例:一个filter对应多个filter-mapping
<filter>
<filter-name>authority</filter-name>
<filter-class>com.bdm.filter.AuthorityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authority</filter-name>
<url-pattern>/pages/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>authority</filter-name>
<url-pattern>/views/*</url-pattern>
</filter-mapping>
2、url-pattern的规则(同样适用于Servlet)
url-pattern用来指定需要Filter拦截的请求的地址,有以下几种匹配的方式:
1️⃣精确匹配:访问地址和url-pattern的地址要一模一样,Filter才会起作用,如/hello/1.jsp,只有访问项目根目录下的hello下的1.jsp时才会走Filter
2️⃣路径匹配:访问的地址是在url-pattern的地址所配置的路径之下,Filter才会起作用,如/hello/*,只要访问项目根目录下的hello下的资源就会走Filter,因此/*会拦截发送到当前项目的所有请求
3️⃣后缀匹配:访问的路径和url-pattern配置的后缀名一致时,Filter才会起作用,如*.abc,只要访问以.abc结尾的路径时,就会走Filter,*.jsp会拦截所有以jsp为后缀的请求
我们所配置url-pattern必须符合以上规则,以上规则不可以混合使用,Servlet的url-pattern同样适用以上规则。
三、Filter的生命周期
Filter的生命周期指的是Filter对象从被创建到被销毁的过程,与生命周期有关的方法:
1、构造器:Filter的构造器在服务器一启动时就被调用,也就是说Filter的实例在服务器启动时就被创建。
2、init()方法:init()在构造器调用之后马上被调用,用来做一些初始化操作,也是在服务器启动之后调用。服务器在调用init()方法时,会传过来一个FilterConfig对象,该对象的作用和ServletConfig差不多,封装了在web.xml中的配置信息,通过FilterConfig可以获取到Filter的别名,可以获取到当前Filter的初始化参数,可以获取到ServletContext对象,并通过ServletContext对象获取Filter全局配置参数信息:
servletContext.getInitParameter("paramName");
3、doFilter()方法:doFilter()方法在Filter每次拦截请求时都会调用,是Filter中的主要方法,服务器在调用doFilter()方法时会传来这么几个参数:
1️⃣ServletRequest:作用和Servlet中的一样,封装请求报文
2️⃣ServletResponse:作用和Servlet中的一样,封装响应报文
3️⃣FilterChain:该对象用来放行拦截的请求,放行方式为:
chain.doFilter(request,response);
4、destroy()方法:destroy在项目卸载之前调用,用来在Filtert对象销毁之前做一些收尾工作
四、Filter的执行顺序
我们可以为一个目标资源设置多个Filter,这样多个Filter就会组成一个Filter链。我们在Filter中调用chain.doFilter(),如果该Filter后还有Filter,就会接着调用下一个Filter的doFilter()方法。如果Filter的顺序是A->B->C,则拦截请求时的顺序就是A->B->C,而响应回到浏览器时的拦截顺序则是C->B->A。
多个Filter的执行顺序是由什么决定的呢?
Filter的执行顺序,是由web.xml中filter-mapping的配置顺序决定的,filter-mapping越靠前,越先执行,我们可以通过调整filter-mapping的顺序,来调整Filter的执行顺序(注意filter-mapping不能在对应的filter标签之前配置):
<!-- Servlet -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
当使用一个Filter去拦截一个Servlet时,将Filter的url-pattern和Servlet的url-pattern设置成一样的即可:
<filter-mapping>
<filter-name>CFilter</filter-name>
<url-pattern>/HelloServlet</url-pattern>
</filter-mapping>
也可以在filter-mapping中设置一个servlet-name标签,将该标签设置为和要拦截的Servlet的name一致
<filter-mapping>
<filter-name>CFilter</filter-name>
<servlet-name>HelloServlet</servlet-name>
</filter-mapping>
五、Filter的dispatcher
Filter默认只会拦截直接发送到目标资源的请求(request请求),而像转发之类的请求它是不会拦截的。如果希望Filter可以拦截转发到目标资源的请求,可以在filter-mapping中的dispatcher标签中配置:
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
dispatcher有四个可选值:
1️⃣REQUEST:指只会拦截直接发送到目标资源的请求,默认值,如果未配置dispatcher则默认就是该值,一旦指定了dispatcher则以指定的属性为准
2️⃣FORWARD:会拦截所有通过转发来访问目标资源的请求
3️⃣INCLUDE:会拦截所有通过动态包含访问目标资源的请求。静态包含<%@ include file=“目标文件”%>不会被拦截,动态包含<jsp:include page="/2.jsp"></jsp:include>会被拦截。只拦截动态包含而不拦截静态包含的原因:静态包含是直接将目标文件包含到原文件中,不需要编译等,而动态包含会在服务器中将jsp文件翻译为.class文件,相当于原页面向目标页面发送了一个请求
4️⃣ERROR:会拦截在web.xml文件中配置的错误页面,可以据此指定一些错误码的错误页面,而不出现很丑的页面
<error-page>
<error-code>404</error-code>
<location>/2.jsp</location>
</error-page>
六、Filter的应用
利用过滤器为请求和响应设置编码:这种设置编码的方式非常好,想改变编码的时候只需要修改配置文件即可
步骤:
①在web.xml中配置编码方式:想改变编码方式时只需要修改value值即可
<context-param>
<param-name>encode</param-name>
<param-value>gbk</param-value>
</context-param>
②在Filter中为请求和响应设置编码:注意该Filter继承了上面的HttpFilter
③在web.xml中配置MyFilter,使其拦截项目中的所有请求
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.bdm.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>