[ SSH框架 ] Struts2框架學習之四(自定義攔截器)


一、Struts2的攔截器

1.1 攔截器概述

   攔截器,在AOP( Aspect-Oriented Programming)中用於在某個方法或字段被訪問之前,進行攔截然后在之前或之后加入某些操作。攔截是AOP的一種實現策略。

  在 Webwork的中文文檔的解釋為—攔截器是動態攔截 Action調用的對象。它提供了一種機制可以使開發者可以定義在一個 action執行的前后執行的代碼,也可以在一個 action執行前阻止其執行。同時也是提供了一種可以提取 action中可重用的部分的方式。

  談到攔截器,還有一個詞大家應該知道—攔截器鏈( Interceptor Chain,在 Struts2中稱為攔截器棧 Interceptor Stack)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其之前定義的順序被調用。

1.2 攔截器實現原理

  大部分時候,攔截器方法都是通過代理的方式來調用的。Struts2的攔截器實現相對簡單。當請求到達 Struts2的ServletDispatcher時, Struts2會查找配置文件,並根據其配置實例化相對的攔截器對象,然后串成一個列表,最后一個一個地調用列表中的攔截器。

  Struts2攔截器是可插拔的,攔截器是AOP的一種實現。Struts2攔截器棧就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時, Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調用。

1.3 Struts2的執行流程

     

1.4 自定義攔截器

  在程序開發中,如果需要開發自己的攔截器類,就需要直接或間接的實現 com.opensymphony.xwork2.interceptor.MethodFilterInterceptor接口,自定義代碼如下:

public interface Interceptor extends Serializable{

        void init();
    void destory();
    String intercept(ActionInvocation invocation) throws Exception;

}    

該接口提供了三個方法,其具體介紹如下。

●  void init( ):該方法在攔截器被創建后會立即被調用,它在攔截器的生命周期內只被調用次.可以在該方法中對相關資源進行必要的初始化。

●  void destroy( ):該方法與init方法相對應,在攔截器實例被銷毀之前,將調用該方法來釋放和攔截器相關的資源。它在攔截器的生命周期內,也只被調用一次。

●  String intercept( ActionInvocation invocation) throws Exception;該方法是攔截器的核心方法,用來添加真正執行攔截工作的代碼,實現具體的攔截操作。它返回一個字符串作為邏輯視圖,系統根據返回的字符串跳轉到對應的視圖資源。每攔截一個動作請求,該方法就會被調用一次。該方法的 ActionInvocation參數包含了被攔截的 Action的引用,可以通過該參數的 invoke方法,將控制權轉給下一個攔截器或者轉給 Action的 execute( )方法。

  如果需要自定義攔截器,只需要實現 interceptor接口的三個方法即可。然而在實際開發過程中除了實現 Interceptor接口可以自定義攔截器外,更常用的一種方式是繼承抽象攔截器類AbstractIntercepter該類實現了 Interceptor接口,並且提供了ini( )方法和 destroy( )方法的空實現。使用時,可以直接繼承該抽象類,而不用實現那些不必要的方法。攔截器類 AbstractInterceptor中定義的方法如下所示:

public abstract class Abstractinterceptor implements Interceptor{

    public void init();

    public void destroy();

    public abstract String intercept(ActionInvocation invocation);

    throws Exception;
}

  從上述代碼中可以看出, AbstractInterceptor類已經實現了 Interceptor接口的所有方法,一般情況下,只需繼承 AbstractInterceptor類,實現 interceptor方法就可以創建自定義攔截器。

  只有當自定義的攔截器需要打開系統資源時,才需要覆蓋 AbstractInterceptor類的 init( )方法和destroy( )方法。與實現 Interceptor接口相比,繼承 AbstractInterceptor類的方法更為簡單。 

1.5 攔截器的配置

●  攔截器

要想讓攔截器起作用,首先要對它進行配置。攔截器的配置是在 struts. xml文件中完成的,它通常以< Interceptor>標簽開頭,以</ interceptor>標簽結束。定義攔截器的語法格式如下:

<interceptor name=interceptorName" class="interceptorclass"">

    <param name="paramname">paramvalue</param>

</interceptor>

上述語法格式中,name屬性用來指定攔截器的名稱, class屬性用於指定攔截器的實現類。有時在定義攔截器時需要傳入參數,這時需要使用< param>標簽,其中name屬性用來指定參數的名稱,paramvalue表示參數的值。

●  攔截器棧

  在實際開發中,經常需要在 Action執行前同時執行多個攔截動作,如:用戶登錄檢查、登錄日志記錄以及權限檢查等,這時,可以把多個攔截器組成一個攔截器棧。在使用時,可以將棧內的多個攔截器當成一個整體來引用。當攔截器棧被附加到一個 Action上時,在執行 Action之前必須先執行攔截器棧中的每一個攔截器定義攔截器棧使用< . interceptors>元素和< Interceptor- stack>子元素,當配置多個攔截器時,需要使用< kinterceptor-ref>元素來指定多個攔截器,配置語法如下:

<interceptors>

    <interceptor-stack name="interceptorstackname">

        <interceptor-ref name="interceptorname"/>

    </interceptor-stack>

</interceptors>

在上述語法中, interceptorstackname值表示配置的攔截器棧的名稱;interceptorName值表示攔截器的名稱。除此之外,在一個攔截器棧中還可以包含另一個攔截器棧。

1.6 編寫自定義攔截器

  下面用一個添加登錄攔截器功能的實例展示自定義攔截器的使用。

●  第一步:創建類,繼承MethodFilterInterceptor類並重寫MethodFilterInterceptor類里的方法寫攔截器邏輯

package com.Kevin.interceptor;
/**
 * 給登錄判斷寫一個自定義攔截器
 */

import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class LoginInterceptor extends MethodFilterInterceptor{

    //編寫攔截器邏輯
    protected String doIntercept(ActionInvocation invocation) throws Exception {                
        //判斷session里是否有username的值
        //得到session
        HttpServletRequest request=ServletActionContext.getRequest();
        Object obj=request.getSession().getAttribute("username");
        //判斷
        //登錄狀態
        //做放行操作,執行action方法
        if(obj!=null)            
            return invocation.invoke();    
        //不是登錄狀態
        //不到登錄,不執行action方法,返回登錄頁面
        //到result標簽里找到login的值,到配置路徑里
        else            
            return "login";    
    }

}

●  第二步:配置action和攔截器關系(注冊攔截器)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
    
<struts>
    <constant name="struts.i18n.encoding" value="UTF-8"></constant>
    
    <package name="strutsdemo" extends="struts-default" namespace="/">
    
        <!-- 1.聲明攔截器 -->
        <interceptors>
            <interceptor name="loginintercept" class="com.Kevin.interceptor.LoginInterceptor"></interceptor>
        </interceptors>
    
        <action name="customer_*" class="com.Kevin.action.LoginAction" method="{1}">
            <!-- 2.使用自定義攔截器 -->
            <interceptor-ref name="loginintercept">
                <!-- 配置action中某些方法不進行攔截
                    name屬性值:excludeMethods
                    值:action不攔截的方法名稱
                 -->
                 <param name="excludeMethods">login</param>
            </interceptor-ref>
            
            <interceptor-ref name="defaultStack"></interceptor-ref>
            
            <!-- 登錄失敗 -->
            <result name="login">/login.jsp</result>
            <!-- 登錄成功 -->
            <result name="loginsuccess">/welcome.jsp</result>
            <result name="menu">/menu.jsp</result>
            
        </action>
        
        <action name="welcome" class="com.Kevin.action.TestAction">
            <result name="success">/welcome.jsp</result>
        </action>    
        
    </package>
    
</struts>

Tips:struts2里有很多默認的攔截器,但是如果在action里配置了自定義攔截器,默認攔截器就不會在執行。

      解決辦法:將默認攔截器手動使用,如代碼<interceptor-ref name="defaultStack"></interceptor-ref>

判斷登錄action編寫:

package com.Kevin.action;
/**
 * 登錄判斷Action
 */
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
    
    //登錄的方法
    public String login(){
        //1.得到request對象
        HttpServletRequest request=ServletActionContext.getRequest();
        String username=request.getParameter("username");
        String password=request.getParameter("password");
        //2.判斷用戶名和密碼
        if("admin".equals(username) && "admin".equals(password)){
            //登陸成功
            request.getSession().setAttribute("username", username);
            return "loginsuccess";
        }
        
        else
            //登錄失敗
            return "login";
    }
    
    //測試攔截器進入menu頁面時,是否進行登錄攔截
    public String menu(){
        return "menu";
    }  
    @Override
    public String execute() throws Exception {
        
        return null;
    }

}

Tips:攔截器和過濾器的區別:(重要概念)

    1.過濾器:理論上可以過濾任意內容,比如html、jsp、servlet、圖片路徑;
    2.攔截器:攔截器可以攔截action。

Tips:Servlet和Action的區別:

    1.servlet默認第一次訪問時候創建,創建一次,單實例對象;
    2.action每次訪問時侯創建,創建多次,多實例對象。

 

二、Struts2的標簽庫

2.1 Struts2標簽庫概述

  對於一個MVC框架而言,重點是實現兩部分:業務邏輯控制器部分視圖頁面部分。 Struts2作為一個優秀的MVC框架,也把重點放在了這兩部分上。控制器主要由 Action來提供支持,而視圖則是由大量的標簽來提供支持。接下來將針對 Struts2標簽庫的構成和常用標簽的使用進行詳細的講解。

2.2 Struts2標簽庫分類

  早期的JSP頁面需要嵌入大量的Java腳本來進行輸出,這樣使得一個簡單的JSP頁面加入了大量的代碼,不利於代碼的可維護性和可讀性。隨着技術的發展,逐漸的采用標簽庫來進行JSP頁面的開發,這使得JSP頁面能夠在很短的時間內開發完成,而且代碼通俗易懂,大大的方便了開發者Struts2的標簽庫就是這樣發展起來的。

  Struts2框架對整個標簽庫進行了分類,按其功能大致可分為兩類,如圖所示。

    

  由圖中可以看出, Struts2標簽庫主要分為兩類:普通標簽UI標簽。普通標簽主要是在頁面生成時,控制執行的流程。UI標簽則是以豐富而可復用的HTML文件來顯示數據。

  普通標簽又分為控制標簽( Control Tags)和數據標簽( Data Tags)。控制標簽用來完成條件邏輯、循環邏輯的控制,也可用來做集合的操作。數據標簽用來輸出后台的數據和完成其他數據訪問功能。

  UI標簽又分為表單標簽( Form Tags)、非表單標簽( non-form Tags)和Ajax標簽。表單標簽主要用來生成HTML頁面中的表單元素,非表單標簽主要用來生成HTML的<div>標簽輸出 Action中封裝的信息等。Ajax標簽主要用來提Ajax技術支持

2.3 Struts2標簽的使用

  Struts2標簽庫被定義在 struts-tags tld文件中,我們可以在 struts-core-2.3.24jar中的META-INF目錄下找到它。要使用 struts2的標簽庫,一般只需在JsP文件使用 taglib指令導入 Struts2標簽庫,具體代碼如下:

<%@taglib prefix="s" uri="/struts-tags" %>

在上述代碼中, taglib指令的uri屬性用於指定引入標簽庫描述符文件的URI, prefix屬性用於指定引入標簽庫描述符文件的前綴。需要注意的是,在JSP文件中,所有的 Struts2標簽庫的使用“s”前綴。

2.4 Struts2的控制標簽

●  <s:if>、<s: elseif>、<s:else>標簽

與多數編程語言中的if、 elseif和else語句的功能類似,<s:if>、<s: elseif>、<s:else>這三個標簽用於程序的分支邏輯控制。其中,只有<s:if>標簽可以單獨使用,而< s elseif>、<s:else>都必須與<sf>標簽結合使用,其語法格式如下所示:

<s: if test="表達式1">

    標簽體

</s: if>

<s:elseif test="表達式2">

    標簽體

</s: elseif>

<s:e1se>

    標簽體

</s :else>

上述語法格式中,<s:if>、和<s: elseif>標簽必須指定test屬性,該屬性用於設置標簽的判斷條件,其值為 boolean型的條件表達式。

●  < s:iterator>標簽

< s:iterator>標簽主要用於對集合中的數據進行迭代,它可以根據條件遍歷集合中的數據。<s:iterator>標簽的屬性及相關說明如表所示:

  

在表中,如果在< s:Iterator>標簽中指定 status屬性,那么通過該屬性可以獲取迭代過程中的狀態信息,如:元素數、當前索引值等。通過 status屬性獲取信息的方法如表所示(假設其屬性值為st):

   

2.5 Struts2的數據標簽

●  < s:property>標簽

<s:property>標簽用於輸出指定的值,通常輸出的是 value屬性指定的值,< s:property>標簽的屬性及屬性說明如下所示:

  ●  id:可選屬性,指定該元素的標識。

  ●  default:可選屬性,如果要輸出的屬性值為nul,則顯示 default屬性的指定值

  ●  escape:可選屬性,指定是否忽略HTM代碼。

  ●  value:可選屬性,指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出 Valuestack棧頂的值。

●  <s:a>標簽

<s:a>標簽用於構造HTML頁面中的超鏈接,其使用方式與 HTML中的<a>標簽類似。<s:a>標簽的屬性及相關說明如表所示:

  

●  <s:debug>標簽

<s:debug>標簽用於在調試程序時輸出更多的調試信息,主要輸出 ValueStack和 StackContext中的信息,該標簽只有一個id屬性,且一般不使用在使用 debug標簽后,網頁中會生成一個[ Debug]的鏈接,單擊該鏈接,網頁中將輸出各種服務器對象的信息,如圖所示:

2.6 Struts2的表單標簽

  Struts2的表單標簽用來向服務器提交用戶輸入的信息,絕大多數的表單標簽都有其對應的HTML標簽,通過表單標簽可以簡化表單開發,還可以實現HTM江中難以實現的功能。大家可以結合HTML的標簽對比學習 Struts2的表單標簽。

● 表單標簽的公共屬性 

Struts2的表單標簽用來向服務器提交用戶輸入信息,在 org. apache struts2 components包中都有個對應的類,所有表單標簽對應的類都繼承自 Uibean類。 Uibean類提供了一組公共屬性,這些屬性是完全通用的。如表所示:

  

  除了這些常用的通用屬性外,還有很多其它屬性。由於篇幅有限,這里就不一一列舉。需要注意的是,表單標簽的name和 value屬性基本等同於HTML組件的name和 value,但是也有些不同的地方:表單標簽在生成HTML的時候,如果標簽沒有設置 value屬性的話,就會從值棧中按照name獲取相應的值,把這個值設置成的HTML組件的 value。簡單的說,就是表單標簽的 value在生成HTML的時候會自動設置值,其值從值棧中獲取。

●  <s:form>標簽

  <s:form>標簽用來呈現HTML語言中的表單元素,其常用屬性如表所示:

  

在使用< s: form>標簽時,一般會包含其它的表單元素,如 textfield, radio等標簽,通過這些表單元素對應的name屬性,在提交表單時,將其作為參數傳入 Struts2框架進行處理。

●  <s:submit>標簽

  < s: submit>標簽主要用於產生HTML中的提交按鈕,該表單元素中,可以指定提交時的 Action對應的方法。通常與< s: forn>標簽一起使用,該標簽的常用屬性如表所示:

  

●  <s:textfield>和<s:textarea>標簽

<s: textfield>和<s: textarea>標簽的作用比較相似,都用於創建文本框,區別在於<s:textfield>創建的是單行文本框,而< s:textarea>創建的是多行文本框。二者使用也比較簡單,一般指定其 label和name屬性即可。兩個標簽的用法如下所示

<s:textfield>標簽的用法:

<s:textfield label="用戶名" name="username"/>

< s:textarea>標簽的用法:

<s:textarea label="Message"  name="message"/>

  name屬性用來指定單行/多行文本框的名稱,在 Action中,通過該屬性獲取單行/多行文本框的值。其 value屬性用來指定單行/多行文本框的當前值。此外,< textarea>標簽可以通過使用cols和rows屬性分別指定多行文本框的列數和行數。

●  <s:password>標簽

  <s:password>標簽用於創建一個密碼輸入框,它可以生成HTML中的<input type="password"/>標簽,常用於在登錄表單中輸入用戶的登錄密碼。<s:password>標簽的常用屬性說明如表所示:

  

  需要注意的是 Struts2的 password標簽與HTML的< input type="password"/>標簽有小小的不同,< input type= "password">標簽只要設置 value屬性就可以將 value屬性的值作為默認顯示值;而 Struts2的< s:password>標簽除了要設置 value屬性,還要設置 showPassword屬性為true。

●  <s:radio>標簽

  < s:radio>標簽用於創建單選按鈕,生成HTML中的< cinput type= radio”個>標簽。< s:radio>標簽的常用屬性說明如表所示:

   

●  <s:checkboxlist>標簽

   <s:checkboxlist>標簽用於一次性創建多個復選框,用戶可以選創建零到多個,它用來產生一組<input type="checkbox"/>標簽,常用屬性說明如表示:

  

●  <s:select>標簽

   <s:select>標簽用於創建一個下拉列表,生成HTML中的<select>標簽,常用屬性說明如表所示:

  

  在表中, headerkey和header value屬性需要同時使用,可以在所有的真實選項之前加一項作為標題項。比如選擇省份的時候,可以在所有的具體省份之前加一項“請選擇”,這個項不作為備選的值。

  multiple屬性和size屬性類似於HTML的< select>標簽,size屬性可以讓下拉框同時顯示多個值multiple屬性讓用戶同時選擇多個值,只是在后台的 Action接收下拉框值的時候,不能使用 String類型,而應該使用 String[ ]或者List<String>。

●  <s:hidden>標簽 

  < s:hidden>標簽用於創建隱藏表單元素,生成HTML中的隱藏域標簽< input type=" hidden”>。該標簽在頁面上沒有任何顯示,可以保存或交換數據。其使用也比較簡單,通常只設置其name和 value屬性即可。其一般用法如下所示:

<s: hidden name="id" value="%{id}"/>

該標簽主要用來需要提交的表單傳值時使用,比如需要提交表單時,要傳一個值到請求參數中去,就可以使用該標簽。

●  <s:reset>標簽

  < s:reset>標簽用來創建一個重置按鈕,會生成HTML中的< input type= "reset"}>標簽,該標簽的使用比較簡單,其常用屬性為name和 value。其中name屬性用於指定重置按鈕的名稱,在 Action中,可以通過name屬性來獲取重置按鈕的值,value屬性用於顯示按鈕的值。該標簽的用法如下所示:

<s:reset value="Reset"/>

 下面通過一個綜合JSP頁面示例來展示form表單標簽的用法:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
  <head>
    <title>My JSP 'form.jsp' starting page</title>
  </head>
  <body>
    <!-- form標簽 -->
    <s:form>
        <!-- 1.普通輸入項 -->
        <s:textfield name="username" label="Username"></s:textfield>
        
           <!-- 2.密碼輸入項 -->
        <s:password name="password" label="Password"></s:password>
        
           <!-- 3.單選輸入項 -->    
        <!-- value屬性值與顯示值一樣 -->
        <s:radio list="{'Male','Female'}" name="gender" label="Gender"></s:radio>
        <!-- value屬性值與顯示值不一樣 -->
        <s:radio list="#{'M':'男','F':'女'}" name="gender2" label="Gender"></s:radio>
        
        <!-- 4.復選輸入項 -->
        <!-- value屬性值與顯示值一樣 -->
        <s:checkboxlist list="{'Basketball','Football','Baseball','PingPang'}" name="hobby" label="Hobby"></s:checkboxlist>
        <!-- value屬性值與顯示值不一樣 -->
        <s:checkboxlist list="#{'Basketball':'BSKTball','Football':'FTball','Baseball':'BSball','PingPang':'PP' }" name="hobby2" label="Hobby"></s:checkboxlist>
        
        <!-- 5.下拉輸入框 -->
        <s:select list="{'C','C++','Java','Python'}" name="language" label="Programming"></s:select>
        
        <!-- 6.隱藏項 -->
        <s:hidden name="hid" label="hidden"></s:hidden>
        
        <!-- 文件上傳 -->
        <s:file name="file" value="Upload"></s:file>
        
        <!-- 7.文本域 -->
        <s:textarea rows="30" cols="60" name="message" label="Message"></s:textarea>
        
        <!-- 8.提交按鈕-->
        <s:submit value="Submit"></s:submit>
        
        <!-- 9.重置按鈕 -->
        <s:reset value="Reset"></s:reset>
    </s:form>

  </body>
</html>

顯示如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM