白話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



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com