Struts2框架的基本使用

weixin_34320159發表於2017-04-29

     前面已經介紹過了MVC思想,Struts2是一個優秀的MVC框架,大大降低了各個層之間的耦合度,具有很好的擴充套件性。從本篇開始我們學習Struts2的基本用法,本篇主要包括以下內容:

  • Struts2的下載安裝
  • 理解整個框架的執行流程
  • 自定義實現Action
  • 自定義配置處理結果

一、下載和安裝Struts2
     登入Apache官網 http://struts.apache.org/download.cgi#struts23163 下載最新版本的Struts,當然建議下載2.3版本的,因為2.5版本剛出來,有些示例應用並不是很全。解壓壓縮包,得到Struts2的原始碼及示例程式碼。

4108288-fa89ee3c122483d7
這裡寫圖片描述

apps目錄中主要是官方提供的Struts2的例項程式碼,對於我們的學習是很有用的。docs中主要是有關Struts2的相關文件內容。lib目錄中主要存放了有關Struts2的核心類庫,以及第三方外掛庫。src中包含了Struts2的全部原始碼。

二、理解Struts2的執行流程
     下面演示一個完整的使用Struts2的例項,目的不是具體的程式碼,重點在於理解整個框架的運作流程。首先我們需要從apps目錄中的struts2-blank示例專案中拷貝出整個lib目錄。(這是使用Struts2最基本的jar包,沒必要從Struts2的lib中一個一個找,因為你也不知道哪些是必需的),我們將他們匯入到我們的專案中。

4108288-acff2e63facbf533
這裡寫圖片描述

這是整個Struts2的請求和響應流程,下面看具體程式碼中是如何體現的。

//web.xml,首先在web.xml中新增如下程式碼,攔截所有請求,即所有請求現在全部歸Struts2框架管了
<filter>
        <filter-name>struts</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
        <filter-name>struts</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>
//編寫Action充當控制器
public class LoginAction extends ActionSupport {
    private String username;
    private String password;
    public String getUsername(){
        return this.username;
    }
    public void setUsername(String s){
        this.username = s;
    }
    public String getPassword(){
        return this.password;
    }
    public void setPassword(String s){
        this.password = s;
    }

    public String execute() throws Exception{

        if(getUsername().equals("walker")&&getPassword().equals("yam")){
            return SUCCESS;
        }
        return ERROR;
    }
}
//新建Struts.xml檔案用於配置框架
<?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>
    <package name="walker" extends="struts-default">
        <action name="login" class="MyPackage.LoginAction">
            <result name="error">/error.jsp</result>
            <result name="success">/index.jsp</result>
        </action>
    </package>
</struts>
/*login.jsp*/
<html>
  <head>
    <title>login</title>
  </head>
  <body>
    <form method="post" action="login">
        姓名:<input type="text" name="username" /><br />
        密碼:<input type="password" name="password" /><br />
        <input type="submit" value="提交"/>
    </form>
  </body>
</html>

我們訪問login.jsp頁面,

4108288-982f039d66ee96ed
這裡寫圖片描述

提交之後會請求URL為login的頁面,框架攔截器攔截,搜尋Struts.xml中該URL所對應的Action控制器,轉向具體的控制器,在我們寫的LoginAction控制器中,我們獲取表單提交的引數並做簡單判斷,返回字串success或者error給核心攔截器。核心攔截器讀取Struts.xml中的配置查詢控制器返回的字串對應的具體檢視位置,forward檢視頁面響應使用者。


4108288-92ebd1825d589d6a
這裡寫圖片描述

這就是一個簡單的Struts2框架的請求和響應過程,可以看到整個框架的核心是主攔截器和各種控制器Action,下面我們具體看看控制器的相關知識。

三、自定義實現Action
     在Action中使用例項變數來封裝請求的引數,正如上面的例子所示:我們在login.jsp頁面提交的username和password兩個引數,對應於LoginAction中的兩個引數,在核心攔截器跳轉LoginAction時,將兩個請求引數自動賦值給LoginAction的兩個例項變數。需要注意的是,對於LoginAction中的這兩個例項變數,是需要提供setter和geter方法的,我們的核心攔截器在跳轉LoginAction的時候也是通過setter方法來對具體的例項引數進行賦值的。
     我們想要自定義xxxAction控制器,需要繼承Action介面,並實現其中的方法。

public interface Action {
    String SUCCESS = "success";
    String NONE = "none";
    String ERROR = "error";
    String INPUT = "input";
    String LOGIN = "login";

    String execute() throws Exception;
}

我們可以看到在Action介面中定義了幾個常用的字串,這些字串會被用於對應物理檢視位置,詳細的內容後文介紹。此處有一個execute方法,這個方法就類似於我們JavaSE中的main方法,一旦核心攔截器攔截請求跳轉到Action頁面,會預設執行execute方法。細心的讀者可能發現,上述的例子中並沒有繼承Action介面,而是繼承了ActionSupport類。其實ActionSupport類還是繼承了Action介面並實現了execute方法,只是ActionSupport類還為我們預設的實現了一些其他的工具函式,方便我們使用,所以基本上在自定義Action的時候會繼承ActionSupport類來減輕編碼難度。

     Struts2中的Action沒有任何和Servlet API耦合的地方,也就是在Action控制器中沒有關於任何可直接操作Servlet API的介面呼叫。對於各個模組之間的分離,Struts還是做的很優秀的。那我們在Action控制器中沒法直接操作Servlet的一些物件,例如:request,response等,但是Struts2框架提供了一個工具類,可以為我們提供這些物件。ActionContext:

static ThreadLocal<ActionContext> actionContext = new ThreadLocal();
//通過靜態工廠建立ActionContext例項物件
public static ActionContext getContext() {
        return (ActionContext)actionContext.get();
    }

//以map的形式設定application範圍內的共享資料
public void setApplication(Map<String, Object> application)
//獲取application範圍內的共享資料
public Map<String, Object> getApplication()
//以map的形式設定session範圍內的共享資料
public void setSession(Map<String, Object> session)
//獲取session範圍內的共享資料
public Map<String, Object> getSession()
//獲取request範圍內的指定的引數值
public Object get(String key)
//向request範圍內新增一個key-value的引數
public void put(String key, Object value)
//獲取request的所有請求引數
public Map<String, Object> getParameters()
//向request範圍內新增一批請求引數
public void setParameters(Map<String, Object> parameters)

我們往往通過ActionContext的靜態方法,通過本地執行緒ThreadLocal獲取ActionContext例項,此ActionContext封裝了有關Servlet操作的各種API呼叫方法。我們看一個簡單的使用:

public class LoginAction extends ActionSupport {
    private String username;
    private String password;
    public String getUsername(){
        return this.username;
    }
    public void setUsername(String s){
        this.username = s;
    }
    public String getPassword(){
        return this.password;
    }
    public void setPassword(String s){
        this.password = s;
    }
    public String execute() throws Exception{
        if(getUsername().equals("walker")&&getPassword().equals("yam")){
            ActionContext ac = ActionContext.getContext();
            ac.put("login","登入成功");
            return SUCCESS;
        }
        return ERROR;
    }
}
<html>
  <head>
    <title></title>
  </head>
  <body>
    <p><%=request.getAttribute("login")%></p>
    <h1>this is the index page</h1>
  </body>
</html>

結果如下:

4108288-7a26a6a81d775c4d
這裡寫圖片描述

以上我們演示瞭如何通過ActionContext 這個工具類來完成對Servlet API的呼叫。其實還可以使用ServletActionContext這個工具類來直接獲取到原Servlet的pageContext,request,response等物件。具體的大家可以自行研究。

四、Action的配置
     以上我們完成了對xxxAction控制器的編寫,但是如果想要我們的核心攔截器能夠在使用者請求URL時,找到對應的Action控制器,我們需要學會在Struts.xml中配置。之前我們介紹過,web.xml是用來配置整個web應用的,那麼我們的struts.xml就是用來配置整個框架的。struts.xml應該被建立並放置在類的載入資料夾中,使用IDE的話,就建立在src資料夾下,在編譯的時候會被拷貝到WEB-INF/classes中。

4108288-6e055404e96a8ba1
這裡寫圖片描述

對於上述的示例,該struts.xml中的內容如下:

<?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>
    <package name="walker" extends="struts-default">
        <action name="login" class="MyPackage.LoginAction">
            <result name="error">/error.jsp</result>
            <result name="success">/index.jsp</result>
        </action>
    </package>
</struts>

     這裡的頭部的一些東西沒必要自己敲,依然從官方給的示例中拷貝即可。至於元素Struts中的內容才是需要自己新增配置的。除去根元素,我們看到的第一個元素是package ,在Struts中使用包來配置Action,也就是所有的Action都必須被配置在一個包下面,當然一個包中也是可以配置多個Action的。
     package元素中可以配置以下屬性:

  • name:該屬性指定了該包的唯一標識,是必須屬性
  • extends:該屬性指定了該包可以繼承別的包,當然別的包中的所有Action這裡都是可用的了,非必須屬性
  • namespace:該屬性指定了該包下的所有Action的名稱空間,主要用於區分同名的Action,非必須屬性
  • abstract:指定了該包是一個抽象的包,抽象的包中是不能定義Action的,但是可以有大量的型別定義、攔截器定義等

從上面給出的Struts.xml中,我們可以看出來,該包繼承了struts-default包,這是struts2-core-2.3.32.3.jar 中的檔案,部分內容如下:

4108288-9eed7e6f88ae91b1
這裡寫圖片描述

被宣告為抽象包之後,其中就定義了一堆攔截器和型別,具體的我們後面介紹,此處只需要知道,我們可以通過包的繼承了來繼承其他包中的一些Action獲取攔截器。由於該包是配置普通Action的基礎,所以一般我們在自定義package的時候會繼承該包。
     接下來我們簡單看看namespace的使用,我們在Struts.xml中可以定義多個包,每個包下面也是可以定義多個Action的,那麼如果某兩個不同的包下面出現同名的Action,框架就自然無法選擇呼叫哪個Action來處理請求。如果我們指定了名稱空間,那麼在請求該包下的Action的時候,就需要帶上名稱空間的值,這樣就可以避免這種衝突。具體看如下例子:

    <package name="walker" extends="struts-default" namespace="a">
        <action name="login" class="MyPackage.LoginAction">
            <result name="error">/error.jsp</result>
            <result name="success">/index.jsp</result>
        </action>
    </package>
    <package name="yam" extends="struts-default" namespace="b">
        <action name="login" class="MyPackage.LoginAction">
            <result name="error">/error.jsp</result>
            <result name="success">/index.jsp</result>
        </action>
    </package>

我們看這兩個包,他們下面配置了相同的Action,但是當時他們具有不同的名稱空間,所以不會產生衝突。例如:我們請求walker包下的login action的URL為:

/a/login

而yam包下的login action的請求URL為:

/b/login

以上我們演示package包下的一些屬性的作用,需要注意一點的是:如果沒有指定namespace的值,則該包下的所有Action都處於預設的名稱空間下,此處預設的名稱空間和 namespace="/" 是有區別的,後者表示該包處於根名稱空間下,而前者中則包含了所有沒有指定namespace值的包。如果框架在根名稱空間或者別的名稱空間下找不到指定的Action,則會前往預設名稱空間下查詢指定了Action。

限於篇幅,未完待續。。

相關文章