过滤器原理及使用
过滤器
Servlet过滤器可以对Servlet、JSP和HTML文件过滤。
过滤器在实际开发中用得较多,是属于较重点的内容。
Servlet过滤器的概念
Servlet过滤器是在Java Servlet规范2.3中定义的,它能够对Servlet容器的请求和响应对象进行检查和修改。
Servlet过滤器本身并不生成请求和响应对象,它只提供过滤作用。
Servlet过滤器能够在Servlet被调用之前检查Request对象,修改Request Header和Request内容。
在Servlet被调用之后检查Response对象,修改Response Header和Response内容。
Servlet过滤器负责过滤的Web组件可以是Servlet、JSP或HTML文件。
Servlet过滤器的过滤过程
过滤器的处理过程是一个链式的过程(FilterChain),即多个过滤器组成一个链,依次处理,最后交给过滤器之后的资源。
其中链式过滤过程中也可以直接给出响应,即返回,而不是向后传递。
Filter接口
所有的Servlet过滤器类都必须实现javax.servlet.Filter接口。
这个接口含有3个过滤器类必须实现的方法:
init(FilterConfig):这是Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例后将调用这个方法。
在这个方法中可以读取web.xml文件中Servlet过滤器的初始化参数。
比如web.xml中声明:
<filter> <filter-name>MyFilter1</filter-name> <filter-class>com.mengdd.filter.MyFilter1</filter-class> <init-param> <param-name>hello</param-name> <param-value>world</param-value> </init-param> <init-param> <param-name>name</param-name> <param-value>zhang</param-value> </init-param> </filter>
在MyFilter1中:
@Override public void init(FilterConfig filterConfig) throws ServletException { String paramValue1 = filterConfig.getInitParameter("hello"); String paramValue2 = filterConfig.getInitParameter("name"); ServletContext context = filterConfig.getServletContext(); }
注意:一旦一个过滤器启动失败,会导致整个Web应用启动失败。
doFilter(ServletRequest, ServletResponse, FilterChain):这个方法完成实际的过滤操作。
当客户请求访问与过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。
FilterChain参数用于访问后续过滤器。
在这个方法中调用chain.doFilter()方法,用于调用过滤器链中后续过滤器的doFilter()方法,假如没有后续过滤器,那么就把客户请求传给相应的Web组件。
如果在这个方法中没有调用chain.doFilter()方法,客户请求不会到达所访问的Web组件。
destroy():Servlet容器在销毁过滤器实例前调用该方法,在这个方法中可以释放Servlet过滤器占用的资源。
过滤器的例子
比如用户登录之后,将信息传入session中,之后的其他页面入口可能需要检测是否存在session,以避免直接在地址栏输入地址访问页面而造成的session中没有用户信息的情况,如果在每个Servlet或JSP前面都要判断session中是否有相应属性,会有很多的代码重复,用过滤器可以很好地解决这个问题,如果不含登录信息,即转向到用户登录页面。
package com.mengdd.filter; 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; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class LoginFilter implements Filter { @Override public void destroy() { // 由Web容器调用 // 在这里进行资源释放 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter invoked!"); HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession();// 获取请求的session,如果没有,会创建一个新的session // 对不需要进行过滤的请求进行事先筛选操作 String requestURI = req.getRequestURI(); if (requestURI.endsWith("login.jsp") || requestURI.endsWith("LoginServlet")) { chain.doFilter(request, response); return; } if (null == session.getAttribute("username")) { // session中没有用户名属性,说明是新的session,之前没有登录 // 用过滤器完成了整个Web应用中未登录情况的处理 ((HttpServletResponse) response).sendRedirect("login.jsp"); // 这里注意,由于本filter配置的<url-pattern>/*</url-pattern>是针对所有地址的 // 所以请求发送到login.jsp页面也会需要先经过此过滤器,造成重定向的递归重复调用 // 所以需要对请求的URI进行判断 } else { // 按照过滤链继续往下走 chain.doFilter(request, response); } } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("Filter init"); // 过滤器是非常特殊的一个Servlet,它会在容器启动的时候得到初始化 // 过滤器的启动失败会导致Web应用启动失败 } }
过滤器的配置和Servlet的配置相似,只不过URL配置的是要过滤请求的URL:
<!-- filter一般配置在所有的Servlet上面 --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>com.mengdd.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- /*表示所有地址,即所有请求都会被送到这个过滤器 -->
可以做很多练习例子,比如可以创建一个NoteFilter过滤器,它可以拒绝列在黑名单上的客户访问留言簿。(黑名单通过过滤器参数设置)。
也可以利用Filter进行一些关键词修改替换的工作。
串联过滤器的例子
几点说明:
1.串联过滤器的顺序是按照在web.xml中的配置顺序为准的。
<filter> <filter-name>MyFilter1</filter-name> <filter-class>com.mengdd.filter.MyFilter1</filter-class> <init-param> <param-name>hello</param-name> <param-value>world</param-value> </init-param> <init-param> <param-name>name</param-name> <param-value>zhang</param-value> </init-param> </filter> <filter> <filter-name>MyFilter2</filter-name> <filter-class>com.mengdd.filter.MyFilter2</filter-class> </filter> <filter-mapping> <filter-name>MyFilter1</filter-name> <url-pattern>/InfoServlet</url-pattern> </filter-mapping> <filter-mapping> <filter-name>MyFilter2</filter-name> <url-pattern>/InfoServlet</url-pattern> </filter-mapping>
2.在Servlet过滤器中能访问application范围内的共享数据:先调用FilterConfig的getServletContext()方法获得ServletContext,再调用ServletContext的getAttribute()方法来获得application范围内的共享数据。
package com.mengdd.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class MyFilter1 implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter1 --> doFilter invoked!"); System.out.println("MyFilter1 --> before chain.doFilter()"); chain.doFilter(request, response); System.out.println("MyFilter1 --> after chain.doFilter()"); } @Override public void init(FilterConfig filterConfig) throws ServletException { String paramValue1 = filterConfig.getInitParameter("hello"); String paramValue2 = filterConfig.getInitParameter("name"); ServletContext context = filterConfig.getServletContext(); } }
package com.mengdd.filter; 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 MyFilter2 implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter2 --> doFilter invoked!"); System.out.println("MyFilter2 --> before chain.doFilter()"); chain.doFilter(request, response); System.out.println("MyFilter2 --> after chain.doFilter()"); } @Override public void init(FilterConfig filterConfig) throws ServletException { } }
参考资料
圣思园张龙老师Java Web视频教程。