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

Posted y-yg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了白话Spring(中级篇)---拦截器(下)相关的知识,希望对你有一定的参考价值。

[一知半解,就是给自己挖坑]

上文我们介绍了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


以上是关于白话Spring(中级篇)---拦截器(下)的主要内容,如果未能解决你的问题,请参考以下文章

Spring AOP深入理解之拦截器调用

记录学习Android基础的心得08:常用控件(中级篇)P2

Spring是个什么?大白话解释下 !

面试官:如何控制Spring事务拦截器的顺序?| Spring系列第50篇

Python+Selenium中级篇之2-Python中类/函数/模块的简单介绍和方法调用

Djanjo中级篇(下)