[一知半解,就是给自己挖坑]
上文我们介绍了Spring中过滤器的基本用法,本文我们来介绍多个拦截器的执行情况,另外一种拦截器的实现方式,以及拦截器与java过滤器的区别。特别的,在本文中,我们将不在演示具体的拦截的实例,请读者们参照上文的实现以及配置方式自行实现。
--------------------------------------------------------------------------------------------------------------------------------------------------------
一。多个拦截器的执行情况。
1.在上文中,我们创建了HelloWorldInterceptor.java文件,在此基础之上,我们再创建一个HelloWorldInterceptor2.java文件,文件与上面的内容基本保持一致,唯一需要修改的是,在输出语句中标识出当前的拦截器名称即可。如下:
System.out.println("preHandle2");
2.在上文的配置文件中,我们演示了如何针对单一的controller进行拦截,现在,我们来介绍如何在全局范围内使用拦截器。具体配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <context:component-scan base-package="interceptor" /> <!-- 视图处理 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> <mvc:interceptors> <bean class="interceptor.HelloWorldInterceptor"></bean> <bean class="interceptor.HelloWorldInterceptor2"></bean> </mvc:interceptors> </beans>
</pre><p></p><pre>
3.保存当前修改,再次启动服务器观察控制台输出即可。
4.多个拦截器的执行顺序。【注,下图来自imooc.com】
-------------------------------------------------------------------------------------------------------------------------------------------------------
二,拦截器的第二种实现---WebRequestInterceptor 接口
1.WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,那么这个WebRequest 是什么呢?这个WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。
示例代码如下:
import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; public class AllInterceptor implements WebRequestInterceptor { /** * 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest中 */ @Override public void preHandle(WebRequest request) throws Exception { // TODO Auto-generated method stub System.out.println("AllInterceptor..............................."); request.setAttribute("request", WebRequest.SCOPE_REQUEST);//这个是放到request范围内的,所以只能在当前请求中的request中获取到 request.setAttribute("session", WebRequest.SCOPE_SESSION);//这个是放到session范围内的,如果环境允许的话它只能在局部的隔离的会话中访问,否则就是在普通的当前会话中可以访问 request.setAttribute("globalSession", WebRequest.SCOPE_GLOBAL_SESSION);//如果环境允许的话,它能在全局共享的会话中访问,否则就是在普通的当前会话中访问 } /** * 该方法将在Controller执行之后,返回视图之前执行,ModelMap表示请求Controller处理之后返回的Model对象,所以可以在 * 这个方法中修改ModelMap的属性,从而达到改变返回的模型的效果。 */ @Override public void postHandle(WebRequest request, ModelMap map) throws Exception { // TODO Auto-generated method stub for (String key:map.keySet()) System.out.println(key + "-------------------------");; map.put("name3", "value3"); map.put("name1", "name1"); } /** * 该方法将在整个请求完成之后,也就是说在视图渲染之后进行调用,主要用于进行一些资源的释放 */ @Override public void afterCompletion(WebRequest request, Exception exception) throws Exception { // TODO Auto-generated method stub System.out.println(exception + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-="); } }
特别注意:与第一种方式实现不同的是,这里的三个返回值类型都是void。即我们不能通过此拦截器进行请求拦截。因此,我们推荐使用功能较为完整的第一种方式。读者可按照自身的需求来选择即可。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
三,拦截器与过滤器的区别
拦截器的常见应用场景:日志记录,权限检查,性能监控,通用行为等AOP常用功能。
过滤器的常见应用场景:统一编码,禁止动态页面缓存,静态资源缓存,自动登陆等。
A.Filter开发示例
1.在上文的工程基础之上,创建MyFilter.java,具体内容如下:
package 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; /** * @ClassName:MyFilter * @Description:filter的三种典型应用: * 1、可以在filter中根据条件决定是否调用chain.doFilter(request, response)方法, * 即是否让目标资源执行 * 2、在让目标资源执行之前,可以对request\response作预处理,再让目标资源执行 * 3、在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能 */ public class MyFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println("----过滤器初始化----"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //对request和response进行一些预处理 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); System.out.println("MyFilter执行前!!!");</span> chain.doFilter(request, response); //让目标资源执行,放行 System.out.println("MyFilter执行后!!!");</span> } public void destroy() { System.out.println("----过滤器销毁----"); } }
2.修改web.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Shiro11</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>*</url-pattern> </filter-mapping> <filter> <filter-name>myFilter</filter-name> <filter-class>filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping> <servlet> <servlet-name>springMvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springMvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
3.在实际开发中,我们可能会使用到过个filter。这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
示例代码:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Demo1过滤前"); System.out.println(filterConfig.getInitParameter("param1")); chain.doFilter(request, response);//放行。让其走到下个链或目标资源中 System.out.println("Demo1过滤后"); }
4.filter的生命周期
创建:
Filter的创建和销毁由WEB服务器负责。
web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作,filter对象只会创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。【请注意观察控制台输出】
销毁:
Web容器调用destroy方法销毁Filter。destroy方法在Filter的生命周期中仅执行一次。在destroy方法中,可以释放过滤器使用的资源。
FilterConfig接口:
用户在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
String getFilterName():得到filter的名称。
String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext():返回Servlet上下文对象的引用。
示例代码:
package me.gacl.web.filter; import java.io.IOException; import java.util.Enumeration; 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 MyFilter02 implements Filter { /* 过滤器初始化 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("----过滤器初始化----"); /** * <filter> <filter-name>MyFilter2</filter-name> <filter-class>filter.MyFilter2</filter-class> <!--配置MyFilter2过滤器的初始化参数--></span> <init-param> <description>配置MyFilter2过滤器的初始化参数</description> <param-name>name</param-name> <param-value>gacl</param-value> </init-param> <init-param> <description>配置MyFilter2过滤器的初始化参数</description> <param-name>like</param-name> <param-value>java</param-value> </init-param> </filter> <filter-mapping> <filter-name>MyFilter2</filter-name> <!--“/*”表示拦截所有的请求 --> <url-pattern>/*</url-pattern> </filter-mapping> */ //得到过滤器的名字 String filterName = filterConfig.getFilterName(); //得到在web.xml文件中配置的初始化参数 String initParam1 = filterConfig.getInitParameter("name"); String initParam2 = filterConfig.getInitParameter("like"); //返回过滤器的所有初始化参数的名字的枚举集合。 Enumeration<String> initParameterNames = filterConfig.getInitParameterNames(); System.out.println(filterName); System.out.println(initParam1); System.out.println(initParam2); while (initParameterNames.hasMoreElements()) { String paramName = (String) initParameterNames.nextElement(); System.out.println(paramName); } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter2执行前!!!"); chain.doFilter(request, response); //让目标资源执行,放行 System.out.println("MyFilter2执行后!!!"); } @Override public void destroy() { System.out.println("----过滤器销毁----"); } }
配置web.xml示例:
<filter> <description>MyFilter过滤器</description> <filter-name>MyFilter</filter-name> <filter-class>MyFilter2</filter-class> <!--配置MyFilter2过滤器的初始化参数--> <init-param> <description>配置MyFilter2过滤器的初始化参数</description> <param-name>name</param-name> <param-value>gacl</param-value> </init-param> <init-param> <description>配置MyFilter2过滤器的初始化参数</description> <param-name>like</param-name> <param-value>java</param-value> </init-param> </filter> <!--映射过滤器--> <filter-mapping> <filter-name>MyFilter02</filter-name> <!--“/*”表示拦截所有的请求 --> <url-pattern>/*</url-pattern> </filter-mapping>
- <description>用于添加描述信息,该元素的内容可为空,<description>可以不配置。
- <filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
- <filter-class>元素用于指定过滤器的完整的限定类名。
- <init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。如果过滤器不需要指定初始化参数,那么<init-param>元素可以不配置。
- <filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
- <filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
- <url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
- <servlet-name>指定过滤器所拦截的Servlet名称。
- <dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。如下:
<filter-mapping> <filter-name>testFilter</filter-name> <url-pattern>/index.jsp</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
<dispatcher> 子元素可以设置的值及其意义:
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
拦截器与过滤器的执行顺序:【注:此图摘自其他博文,详情见参考资料】
拦截器与过滤器的对比:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
特别备注:
Filter的执行顺序是按照web.xml中的配置顺序执行的。
-------------------------------------------------------------------------------------------------------------------------------------
至此,白话Spring(中级篇)---拦截器(下)结束
参考资料:
http://www.cnblogs.com/xdp-gacl/p/3948353.html
http://blog.csdn.net/chenleixing/article/details/44573495
http://www.cnblogs.com/dreamroute/p/4198087.html?utm_source=tuicool
http://haohaoxuexi.iteye.com/blog/1750680