您的位置:首页 > 编程语言 > Java开发

白话Spring(中级篇)---拦截器(下)

2016-04-04 12:05 609 查看
[一知半解,就是给自己挖坑]

上文我们介绍了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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: