一、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>
顯示如下: