1. 程式人生 > >關於學習struts2的總結與心得

關於學習struts2的總結與心得

在此處我將記錄一些我學習struts2時總結的要點,分章節與學習進度保持同步,與君共勉。

注:視訊學習資料來自北京尚學堂培訓公司-馬士兵所有。

開發環境:IDE為IDEA 2016.2.5,JDK1.8,struts版本為2.1.6,Tomcat為7.0.75

學習資料下載:點我下載(涵蓋原始碼+視訊)

=============================================================

如何使用struts2

1.將7個jar包匯入到專案lib中去,IDEA需在WEB-INFO資料夾下新建資料夾lib,匯入jar包,右鍵資料夾--add as librery

--完成


2.編寫web.xml配置檔案,將以下程式碼插入至<web-app>標籤中

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3.匯入struts.xml配置檔案至專案src根目錄

4.建立JavaAction類進行操作

=====================================================================

名稱空間

namespace決定了action的訪問路徑,也就是namespace裡面寫了什麼,你的請求位址列裡就必須要包括什麼。

namespace預設為“”,可以接收到所有路徑的action,可以寫為/,/xxx,/xxx/yyy 對應的訪問路徑就應該為/xxx/xxx.action,/xxx/yyy/xxx.action

namespace最好用模組來命名。

如下程式碼:

    <constant name="struts.devMode" value="true" />
    <package name="login" namespace="/login" extends="struts-default">
        <action name="login">
            <result>/login_success.jsp</result>
        </action>
    </package>

constant name="struts.devMode" value="true" 為除錯模式開啟(IDEA無法呼叫此功能,除錯須重新部署)

package *name*屬性用來區分重名的情況。


=====================================================================

Action

struts配置檔案內的Action標籤為你的Java操作的導航(個人理解)與返回值的判定和頁面的跳轉的集合體

struts.xml內程式碼如下:

    <package name="path" namespace="/path" extends="struts-default">
        <action name="path" class="test.example">
            <result name="success">
                /path.jsp
            </result>
        </action>
    </package>
action標籤裡的class值為你在src目錄建立的JavaAction類的路徑

Action操作類我用繼承了一個ActionSupport類,類裡包含了一些預設好了的物件和屬性供開發者利用,後續struts2的Action類也都將繼承此類。

在此處我過載了ActionSupport裡的execute()方法,返回了一個值為“success”的String物件。

execute()為預設執行的方法,即在上面的配置檔案裡的action標籤內,你不宣告method屬性時預設執行的方法。

example.java程式碼如下:

package test;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/8.
 */
public class example extends ActionSupport{

    @Override
    public String execute(){
        return "success";//result預設值為success
    }
}
這個action類返回一個“success”的String至struts.xml,裡面的result標籤負責接收這個值,其中name屬性為結果值,預設為"success" 

也就是說 若返回值為success,那麼result標籤內就不用再寫name屬性。

另外一種action類的的寫法,此方法為主流方法,可以自定義方法名來進行開發操作,而不僅僅侷限與execute()這一種方法。

另外返回值我們利用了SUCCESS這一在父類ActionSupport中介面Action中的變數值,

可看到struts2的原始碼中為我們內建了這些變數值:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.opensymphony.xwork2;

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

    String execute() throws Exception;
}

Action程式碼如下:

package action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/9.
 */
public class CourseAction extends ActionSupport {
    public String add(){
        return SUCCESS; //介面包含變數
    }
    public String delete(){
        return SUCCESS;
    }
}
可以看到此處我們不在過載execute方法,而是選擇自定義命名的方法,那麼在struts.xml裡面我們應該如何接受返回值?請看
    <package name="actions" namespace="/actions" extends="struts-default">
        <action name="course" class="action.CourseAction" method="add">
            <result>
                /User_add_success.jsp
            </result>
        </action>
        <action name="course" class="action.CourseAction" method="delete">
            <result>
                /User_delete_success.jsp
            </result>
        </action>
    </package>

可以看到,在此處action標籤內我們編寫了一個method屬性,裡面的值就是我們action類裡自定義方法的名稱

這樣一來,我們就可以通過這種方法來呼叫自定義的方法了。


=====================================================================

萬用字元

上面的程式碼大家有沒有覺得很冗餘,其實我們可以精簡一點萬用字元的運用:

struts.xml

<package name="actions" namespace="/actions" extends="struts-default">
        <action name="*_*" class="action.{1}Action" method="{2}" >
            <result>
                /{1}_{2}_success.jsp
            </result>
        </action>
</package>
可以看到action的name值變為了*_*, 這些*就代表了我們要傳(要變)的引數,可以為add,可以為delete等等,後面的{1}{2}分別代表了第一個*所代表的值與第二個*所代表的值,依次代入,就可以實現動態接受與判斷資料了。

配置程式碼是不是精簡了很多?但是需要遵循約定優於配置這個原則

對應action類與jsp頁面



=====================================================================

 用Action的屬性接收引數

在JSP頁面內,我們如果需要傳引數至後端,該如何進行? 如:超連結可以這一樣寫 http://localhost:8080/xxxxx/user/user!add?name=xxx&age=xxx 其中方法名寫在!後面,需要傳的引數則寫在?後面與平常無異。 在Action類中,我們需要宣告變數用來接收引數,變數名最好與引數名一致(也可不一致,get set方法名與引數名一致即可,但不提倡) 並編寫get() set() 方法。 UserAction.java寫法:
package action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/9.
 */
public class UserAction extends ActionSupport{
    private String name; 
    private int age;

    public String add(){
        System.out.println("name:"+name);
        System.out.println("age:"+age);
        return SUCCESS;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

可以看到這種方式可以接收到頁面傳遞過來的引數值,並進行處理。但是這種傳參模式有缺點,就是如果變數過多,會導致程式碼異常冗餘 針對這點,下面這種方法就對其進行了優化。

用DomainModel接受引數

首先我們需要編寫一個實體類,包含我們需要用到的資料,如:
package bean;

/**
 * Created by Alex on 2017/5/9.
 */
public class User {
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
我們在頁面內進行傳值的地址連結更改為如下這種: http://localhost:8080/user/user!add?user.name=xxx&user.age=xxx 將傳的引數前加上了我們實體類的名字user,然後.引數名
我們再來看看Action類是如何編寫的:
package action;

import bean.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/9.
 */
public class UserAction2 extends ActionSupport{
    private User user; //例項化User實體類
    public String add(){
        System.out.println("username:"+user.getName());
        System.out.println("username:"+user.getAge());
        return SUCCESS;
    }
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
我們可以理解為,引數傳過來,呼叫了set方法,我們使用get方法取值。 在這種方式下,我們使用實體類載入物件,然後在Action裡實例化,再直接呼叫實體類的get方法,就可取到頁面傳過來的引數了。
這種方式我們稱之為 DomainModel 預模型

DTO(VO\DO)

如果我們需要傳的引數並不是所有都需要用到,那這些引數我們應該如何處理呢? Data Transform Object(DTO)資料傳輸物件 就可以解決此問題

用ModelDriven接收引數

傳參地址仍然為: http://localhost:8080/test/user/user!add?name=xxx&age=xxx 他的Action 是這樣寫的
package action;

import bean.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

/**
 * Created by Alex on 2017/5/11.
 */
public class UserAction3 extends ActionSupport  implements ModelDriven<User>{
    private User user = new User();

    public String add(){
        System.out.println("name"+user.getName());
        System.out.println("name"+user.getAge());
        return SUCCESS;
    }
    @Override
    public  User getModel(){
        return user;
    }
}


可以看到,在Action裡我們使用了一個叫ModelDriven的介面。這樣一來在Action內我們可以過載他的getModel方法,一旦成功 就可以直接使用set方法來傳引數至實體類,進而可用get方法來獲取引數進行操作。 這種方法實際使用並不多,學習它是因為它包含了一個很重要的設計概念,即MVC模型思想 struts2代表了MVC中的Controller部分 以上三種傳參方式,最為常用的為DomainModel這種方式,其餘兩種瞭解概念即可

2.1.6版本的中文問題

在傳參過程中,我們會發現一個問題,如果我們輸入中文,那麼傳過來的引數會是亂碼,這是因為專案本身編碼不支援中文,我們需要更改部分配置來進行轉碼。 我們可以在struts配置檔案插入以下配置,可完成中文轉碼(僅支援struts版本為2.1.7以上,以下的會有BUG)
<constand name=“struts.i18n.encoding" value="utf-8" />
若版本在2.1.7以下,則可用過濾器的方式來進行轉碼 post請求轉碼過濾器寫法:
package filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * Created by Alex on 2017/5/11.
 */
@WebFilter(filterName = "EncodingFilter")
public class EncodingFilter implements Filter {
    public void destroy() {
    }//過濾器銷燬

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }//過濾器初始化

}


get請求轉碼需要更改Tomcat根目錄下server.xml中的 具體位置:tomcat根目錄/conf/server.xml 找到
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"  redirectPort="8443" />
更改為:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"  URLEncoding="utf-8" />
即可修復中文輸入亂碼問題

簡單的資料驗證


我們傳入的引數需要進行簡單的資料驗證。接下來我將利用上面的例子進行驗證。 首先我們來配置struts.xml檔案
    <package name="actions" namespace="/actions" extends="struts-default">
        <action name="user2" class="action.UserAction2">
            <result> /User_add_success.jsp </result>
            <result name="error">/user_add_error.jsp</result>
        </action>
    </package>
此處有兩個result代表了兩種處理情況,成功與失敗 Action類:
package action;


import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/11.
 */
public class LoginAction  extends ActionSupport {
    private String name;

    public String add(){
       if(name==null || !name.equals("fjnmbb12")){
            this.addFieldError("name","name is error");
            return ERROR;
        }
        return SUCCESS;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
可以看到這裡add()內有兩種情況,返回了不同的結果。其中發生錯誤的結果內有一個addFieldError方法,此方法將會發送錯誤的原因至前端頁面。 請看前端頁面寫法:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/11
  Time: 22:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    使用者新增異常!
    <s:fielderror fieldName="name" theme="simple" /> <br/>
    <s:property value="errors.name[0]"/> <br/>
    <s:debug></s:debug>
</body>
</html>
在前端頁面我們引用了struts 的標籤,首先在頂部插入引用語, 在內容中即可呼叫struts標籤。 <s:fielderror>標籤是輸出錯誤原因,但是有標準格式(不可改) <s:property>標籤是可輸出多個錯誤原因,以Map的方式輸出(第一個下標為0),並可自定義格式,擷取長度等 ,比較常用。 <s:debug>是嵌入一個超連結 點選可開啟struts的除錯內容

訪問Web元素

struts取request,response(一般不用),session,application的方式 我們先編寫前端JSP頁面的HTML程式碼,如下所示:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/10
  Time: 16:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <base href="http://localhost:8080/">
    <title>$Title$</title>
  </head>
  <body>
  <form action="" id="f" name="f" method="post">
    <input type="text" name="name" placeholder="輸入使用者名稱" /><br />
    <input type="password" name="password" placeholder="輸入密碼" /> <br />
    <input type="button" value="提交1" onclick="javascript:document.f.action='login/login1';document.f.submit();"  />
    <input type="button" value="提交2" onclick="javascript:document.f.action='login/login2';document.f.submit();"  />
    <input type="button" value="提交3" onclick="javascript:document.f.action='login/login3';document.f.submit();"  />
    <input type="button" value="提交4" onclick="javascript:document.f.action='login/login4';document.f.submit();"  />
  </form>
  </body>
</html>
效果如下圖: struts配置檔案程式碼:
    <package name="login" namespace="/login" extends="struts-default">
        <action name="login*" class="action.LoginAction{1}">
            <result>/login_success.jsp</result>
        </action>
    </package>
第一種訪問的方式,為呼叫ActionSupport內的ActionContext.getContext()方法進行取值,然後過載execute()方法傳值。 三大物件在本質上還是Map。 Action1程式碼:
package action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

import javax.net.ssl.SSLSessionContext;
import java.util.Map;

/**
 * Created by Alex on 2017/5/10.
 */
public class LoginAction1 extends ActionSupport{
    private Map request;//宣告型別為Map的request變數
    private Map session;//宣告型別為Map的session變數
    private Map application;//宣告型別為Map的application變數

    public LoginAction1(){
        //呼叫ActionSupport內的方法對三大物件進行取值
        request = (Map)ActionContext.getContext().get("request");
        session=ActionContext.getContext().getSession();
        application = ActionContext.getContext().getApplication();
    }
    @Override
    public String execute(){
        request.put("r1","r1");
        session.put("s1","s1");
        application.put("a1","a1");
        return SUCCESS;
    }
}

context:環境,周圍環境 第二種訪問取值的方式,依賴於struts2, 實現了幾個叫做RequestAware,SessionAware,ApplicationAware的介面,不需要再從context中取值。 此處涉及到一個非常重要的設計思想(Spring框架會提到)叫做IoC,全稱Inverse of Control(控制反轉),也成為DI,全稱Dependency Injection(依賴注入
實現了這三個介面後,請求可直接呼叫set方法訪問web元素,非常方便。 Action2程式碼:
package action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;

import java.util.Map;

/**
 * Created by Alex on 2017/5/10.
 */
public class LoginAction2 extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{

    private Map<String,Object> request;
    private Map<String,Object> session;
    private Map<String,Object> application;

    public String execute(){
        request.put("r1","r2");
        session.put("s1","s2");
        application.put("a1","a2");
        return SUCCESS;
    }

    @Override
    public void setRequest(Map<String, Object> request) {
        this.request = request;
    }

    @Override
    public void setSession(Map<String, Object> session) {
        this.session = session;
    }

    @Override
    public void setApplication(Map<String, Object> application) {
        this.application = application;
    }

}

第一種方式是主動去訪問主動去拿的,第二種方式是被動詢問取值,若實現了介面,則可直接訪問與取值。這就是依賴注入的思想。後者是最常用的方法。 其餘2種過於簡單,不再此論述。

Action總結

Action:
  • 實現一個Action的最常用的方式:從ActionSupport繼承
  • DMI動態方法呼叫 !xxx
  • 萬用字元配置 * {1} {2}
  1. *_*
  • 接收引數的方法(一般用屬性和DomainModel來接收)
  • 簡單的資料驗證 addFieldError
  1. 一般不使用struts2的ui標籤
  • 訪問WEB元素
  1. Map型別(1、IoC  2、依賴struts2)
  2. 原始型別 (1、IoC 2、依賴struts2)
  • 包含檔案配置(include標籤)
  • 預設action處理

結果型別

result的type型別:
  1. dispatcher
  2. redirect
  3. chain
  4. redirectAction
上面四種result型別是我們常用的,下面我將配置程式碼編寫如下: struts.xml:
<package name="resultTypes" namespace="/r" extends="struts-default">
        <action name="r1">
            <result type="dispatcher"> /r1.jsp </result>
        </action>

        <action name="r2">
            <result type="redirect">/r2.jsp</result>
        </action>

        <action name="r3">
            <result type="chain">r1</result>
        </action>

        <action name="r4">
            <result type="redirectAction">r2</result> 
        </action>
</package>
我寫了4個Action,每一個Action都對應了一個結果型別 第一個Action是預設的跳轉結果型別,會直接在請求伺服器再跳轉,其訪問地址不變,為 http://localhost:8080/r/r1 第二個Action是客戶端之間的跳轉,其地址將變為: http://localhost:8080/r2.jsp 第三個Action的結果型別訪問的是r1,而r1是跳轉至r1.jsp。所以其地址為:
http://localhost:8080/r/r1 第四個Action的結果型別訪問的是r2,而r2是跳轉至r2.jsp,其結果地址為:
http://localhost:8080/r2.jsp 注意:使用chain時,若是需要使用另外一個包的action,那麼就需要在result標籤內寫上param標籤 如:
<action name="r3">
            <result type="chain">
                <param name="actionName">actionName</param>
                <param name="/namespace">namespace</param>
            </result>
</action>

全域性結果集

定義一個可以共用的結果集,配置如下:
 <package name="user" namespace="/user" extends="struts-default">

        <global-results>
            <result name="mainpage">/main.jsp</result>
        </global-results>

        <action name="user" class="action.userAction">
            <result> /user_success.jsp </result>
            <result name="error">/user_error.jsp</result>
        </action>
 </package>
Action程式碼:
package action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.inject.Scope;

/**
 * Created by Alex on 2017/5/15.
 */
public class userAction extends ActionSupport {
    private int type;

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    @Override
    public String execute() throws Exception{
        if(type==1)return SUCCESS;
        else if(type == 2) return ERROR;
        else return "mainpage";
    }
}
那麼只要是返回值為mainpage時,都會跳轉至:main.jsp頁面 如果其他的包要用這個全域性結果集,那麼就需要用到extends,程式碼如下:
    <package name="admin" namespace="/admin" extends="user">
        <action name="admin" class="action.adminAction">
            <result>/admin.jsp</result>
        </action>
    </package>
Action程式碼如下:
package action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/15.
 */
public class adminAction extends ActionSupport {
    @Override
    public String execute(){
        return "mainpage";
    }
}
這樣配置,使得我們新包繼承了原來的包的全域性結果集,所以可以使用該結果集。

動態結果集

使用特殊標誌來動態接受結果集 struts配置如下:
 <constant name="struts.devMode" value="true" />
    <package name="user" namespace="/user" extends="struts-default">
        <action name="user" class="action.userAction">
            <result>${r}</result>
        </action>
    </package>
可以看到我們的result標籤內跳轉的頁面名改為了${r}的形式,為何使用這種結構呢? 請看Action的程式碼:
package action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.inject.Scope;

/**
 * Created by Alex on 2017/5/15.
 */
public class userAction extends ActionSupport {
    private String r;
    private int type;

    public String getR() {
        return r;
    }

    public void setR(String r) {
        this.r = r;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    @Override
    public String execute() throws Exception{
        if(type==1)r="/user_success.jsp";
        else if(type==2)r="/user_error.jsp";
        return SUCCESS;
    }
}


可以看到我們action程式碼裡,定義了兩個變數,生成了2個get and set方法,他們分別為type和r type就是我們傳進來的型別,r就是我們在action內定義的跳轉頁面名 可是為何我們能在struts.xml內接收到這個r的值呢? 原來struts訪問了valueStack,值棧,我們在action內將r存進值棧了,在struts那邊就可以用${r}來取到。 可以看到我們的r值就在這裡面。 p.s 這種表示式名稱為 OGNL表示式

帶引數的結果集

使用ONGL表示式傳引數,struts配置檔案如下:
    <package name="user" namespace="/user" extends="struts-default">
        <action name="user" class="action.userAction">
            <result type="redirect"> /user_success.jsp?t=${type}</result>
        </action>
    </package>
可以看到在result內,我們傳過來的引數使用了ONGL表示式來接收 接受jsp頁面程式碼:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/15
  Time: 22:05
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>user success</title>
</head>
<body>
user success!! <br/>
from valueStack:<s:property value="r" /><br/>
from actionContext: <s:property value="#parameters.t"/>
<s:debug></s:debug>
</body>
</html>

效果如圖
具體關係圖:
可以看到傳過來的引數我們並沒有儲存至值棧內,而是直接傳到頁面了,所以我們用
<s:property value="t" />
並不能取到值,而用
<s:property value="#parameters.t"/>
就能從actionContext裡將t值取出 關係如下圖: 在value stack內並沒有t的值 但是在stack context內就能找到t的值

結果集(result)總結

Result:
  • 常用的四中型別:
  1. dispatcher(預設)
  2. redirect
  3. chain
  4. redirecAction
  • 全域性結果集
  1. global-results || extends(繼承另外一個包的結果集)
  • 動態結果(瞭解即可)
  1. 在action中儲存一個屬性,儲存具體的結果location
  • 傳遞引數
  1. 客戶端跳轉才需要傳遞
  2. ${}(ONGL表示式 不是EL表示式) 從valueStack取值

OGNL

此部分我們使用一個小專案來進行講解 剛開始我們配置一下檔案,包含兩個JSP頁面 index.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/16
  Time: 18:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首頁</title>
  </head>
  <body>
  訪問屬性:<br/>
  <a href="ognl.action?username=alex&password=123">ognl</a>
  </body>
</html>


ognl.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/16
  Time: 18:01
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>OGNL表示式語言學習</title>
</head>
<body>
<ol>
在此插入例子
</ol>
<s:debug></s:debug>
</body>
</html>

以及struts.xml配置檔案:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
    <constant name="struts.devMode" value="true" />
    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    <include file="/ognl/ognl.xml" />
</struts>

和外部ognl.xml配置檔案:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
        "http://struts.apache.org/dtds/struts-2.1.dtd">

<struts>
    <package name="ognl" extends="struts-default">
        <action name="ognl" class="ognl.ognlAction" >
            <result>/OGNL.jsp</result>
        </action>
    </package>
</struts>


以及JAVA類:
  1. ognlAction.java
    package ognl;
    
    import com.opensymphony.xwork2.ActionSupport;
    
    import java.util.*;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class ognlAction extends ActionSupport {
        private String username;
        private String password;
        private User user;
        private Cat cat;
        private List<User> users = new ArrayList<User>();
        private Set<Dog> dogs = new HashSet<Dog>();
        private Map<String,Dog> dogMap = new HashMap<String , Dog>();
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public ognlAction() {
            users.add(new User(1));
            users.add(new User(2));
            users.add(new User(3));
    
            dogs.add(new Dog("dog1"));
            dogs.add(new Dog("dog2"));
            dogs.add(new Dog("dog3"));
    
            dogMap.put("dog100",new Dog("dog100"));
            dogMap.put("dog101",new Dog("dog101"));
            dogMap.put("dog102",new Dog("dog102"));
        }
    
        public List<User> getUsers() {
            return users;
        }
    
        public void setUsers(List<User> users) {
            this.users = users;
        }
    
        public Set<Dog> getDogs() {
            return dogs;
        }
    
        public void setDogs(Set<Dog> dogs) {
            this.dogs = dogs;
        }
    
        public Map<String, Dog> getDogMap() {
            return dogMap;
        }
    
        public void setDogMap(Map<String, Dog> dogMap) {
            this.dogMap = dogMap;
        }
    
        @Override
        public String execute(){
            return SUCCESS;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String m(){
            return "hello!";
        }
    }
    

  2. User.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class User {
        private int age = 8;
    
        public User(){
        }
    
        public User(int age){
            super();
            this.age=age;
        }
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User:" +age ;
        }
    }
    

  3. Cat.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class Cat {
        private Dog friend;
    
        public Dog getFriend() {
            return friend;
        }
    
        public void setFriend(Dog friend) {
            this.friend = friend;
        }
    
        public String miaomiao(){
            return "miaomiao~";
        }
    }
    

  4. Dog.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class Dog {
        private String name;
        public Dog(){}
        public Dog(String name){
            super();
            this.name = name;
        };
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Dog:" + name;
        }
    }
    
    

  5. S.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class S {
        public static String STR="static string";
    
        public static String s(){
            return "static method";
        }
    }
    

此處我們使用迭代式開發(一點點來,步步為營,可調式,士氣高漲,主流開發模式) 注意事項:
  1. OGNL全稱 Object Gragh Navigation Language,物件圖導航語言
  2. 1.user.xxx 只有傳,才會構造(在頁面內只有傳了引數進去,值棧裡面才會有值) 當然可以直接new物件出來,預設變數值,效果一樣(domain model)。但是需要保持一個無參構造方法
ONGL如何訪問物件和action的普通屬性與普通方法:
    <li>訪問值棧中的Action的普通屬性:username = <s:property value="username"/> </li>
    <li>訪問值棧中的物件的普通屬性(get set方法) :<s:property value="user.age"/></li>
    <li>訪問值棧中的物件的普通屬性(get set方法): <s:property value="cat.friend.name" /></li>
    <li>訪問值棧中的物件的普通方法: <s:property value="password.length()" /></li>
    <li>訪問值棧中的物件的普通方法: <s:property value="cat.miaomiao()" /></li>
    <li>訪問值棧中的action的普通方法: <s:property value="m()" /></li>
如何訪問靜態屬性與方法:
<li>訪問靜態方法: <s:property value="@[email protected]()" /></li>
<li>訪問靜態屬性: <s:property value="@[email protected]" /></li>
<li>訪問Math類的靜態方法: <s:property value="@@max(2,3)" /></li>
P.S: 第三種方法不常用。 如何訪問普通類的構造方法:
<li>訪問普通類的構造方法: <s:property value="new ognl.User(8)" /></li>
P.S:在此處可以直接在value屬性裡new物件出來 如何訪問各種集合:
    <li>訪問List: <s:property value="users" /></li>
    <li>訪問List中的某個元素: <s:property value="users[1]" /></li>
    <li>訪問List中的某個元素集合: <s:property value="users.{age}" /></li>
    <li>訪問List中的某個元素集合中的特定值:<s:property value="users[0].age" /> (最常用) ||  <s:property value="users.{age}[0]" /> </li>
    <li>訪問Set: <s:property value="dogs" /></li>
    <li>訪問Set中的某個元素: <s:property value="dogs[1]" /></li>
    <li>訪問Map: <s:property value="dogMap" /></li>
    <li>訪問Map中的某個元素: <s:property value="dogMap.dog101" /> || <s:property value="dogMap['dog101']"/> | <s:property value="dogMap[\"dog101\"]" /> </li>
    <li>訪問Map中的所有key: <s:property value="dogMap.keys" /></li>
    <li>訪問Map中的所有value: <s:property value="dogMap.values" /></li>
    <li>訪問Map容器的大小: <s:property value="dogMap.size()" /></li>
    <li>訪問Set容器的大小: <s:property value="dogs.size()" /></li>
    <li>訪問List容器的大小: <s:property value="users.size()" /></li>
P.S:其中訪問Set中的某個元素時,並取不到值,因 set集合中元素位置不固定所導致 投影:
    <li>投影(過濾):<s:property value="users.{?#this.age==1}.{age}" /></li>
    <li>投影:<s:property value="users.{^#this.age>1}.{age}" /></li>
    <li>投影:<s:property value="users.{$#this.age>1}.{age}" /></li>
    <li>投影:<s:property value="users.{$#this.age>1}.{age} == null" /></li>
P.S: ^為開頭第一個,$為最後一個,而加判斷式 == 輸出true or false 

JSP頁面:


struts標籤

要點集合:
  •  通用標籤
  1. property
  2. set (預設為action scope 會將值放入request 和 ActionContext中  ;   page,request,session,application)
  3. bean
  4. include(對中文檔案有支援問題,不建議使用,如需包含,改用jsp包含)
  5. param
  6. debug
  • 控制標籤
  1. if、elseif、 else
  2.  iterator(collections map  enumeration iterator array,迴圈遍歷)
  3. subset(擷取集合一部分)
  • UI標籤
  1. theme(simple xhtml:預設 、 css xhtml、 ajax )
  • AJAX標籤
  1. 再補充
  • $  #  %的區別
  1. $用於 i18n 和 struts配置檔案
  2. #取得ActionContext的值
  3. %將原本的文字屬性解析為ognl,對於本來就是ongl的屬性不起作用(參考<s:property>和<s:include>)
在此我們還是使用小專案的形式來進行總結: 首先我們需要配置幾個jsp頁面 index.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/16
  Time: 23:15
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首頁</title>
  </head>
  <body>
  訪問屬性:<br/>
  <a href="tags.action?username=alex&password=123">tags</a>
  </body>
</html>
在此處我們傳了兩個引數(username和password)至struts.xml

tags.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/17
  Time: 5:21
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<ol>
   在此處寫例子
</ol>
</body>
</html>


在配置完成後,再看看struts.xml是如何配置的:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <constant name="struts.devMode" value="true" />
    <package name="tags" extends="struts-default">
            <action name="tags" class="tags.tagsAction">
                <result>/tags.jsp</result>
            </action>
    </package>
</struts>

然後是action: tagsAction:
package tags;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/17.
 */
public class tagsAction extends ActionSupport {
    private String username;
    private String password;

    public tagsAction(){
    }

    @Override
    public String execute(){
        this.addFieldError("fieldError.test","wrong!");
        return SUCCESS;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
注意學習方法: 儘量多查閱官方DOC文件,幫助很大。 通用標籤: struts標籤的property標籤 官方文件:
頁面程式碼:
    <li>property: <s:property value="username" /> </li>
    <li>property 取值為字串: <s:property value="'username'" /> </li>
    <li>property 設定預設值: <s:property value="admin" default="管理員" /> </li>
    <li>property 設定HTML: <s:property value="'<hr/>'" escape="false" /> </li>
P.S:value的屬性為object型別,都會把裡面的物件解析為OGNL表示式。設定預設值意思是,當property在值棧中找不到物件,則會使用這個預設值。 struts標籤的set標籤 官方文件:
頁面內容:
    <li>set 設定adminName值(預設為request和ActionContext):<s:set var="adminName" value="username" /> var adminName = username</li>
    <li>set 從request裡取值 :<s:property value="#request.adminName" /></li>
    <li>set 從ActionContext裡取值 :<s:property value="#adminName" /></li>
    <%--<li>set 設定範圍:<s:set name="adminPassword" value="password" scope="page" /> name adminPassword = password (page) </li>--%>
    <%--<li>set 從相應的範圍取值:<%=pageContext.getAttribute("adminPassword")%></li>--%>
    <li>set 設定var 範圍為ActionContext: <s:set name="adminPassword" value="password" var="a"/> var a = password(廢棄) </li>
    <li>set 使用#取值: <s:property value="#a" /> </li>
    <li>set 設定var 範圍為Session: <s:set var="adminPassword" value="password" scope="session"/> var adminPassword = password(主流) </li>
    <li>set 從相應的範圍取值: <s:property value="#session.adminPassword" /> </li>
P.S:這裡的value的屬性也為object型別,官方doc文件有bug。  id與name屬性已經廢棄,可以不使用。 var使用時,預設為Action,但是用 scope可以指定特定的儲存空間。 struts標籤的bean標籤 官方文件:
頁面內容:
     <li>
        bean 定義bean,並使用param來設定新的屬性值:
        <s:bean name="tags.Dog" var="myDog">
            <s:param name="name" value="'linda'"></s:param>
        </s:bean>
    </li>
    <li>bean 取值:<s:property value="#myDog.name" /> </li>
    <li>bean 檢視debug:<s:debug></s:debug></li>
P.S:param子標籤內部的value值我們若需要插入字串格式,那麼需要在裡面加上單引號,若是需要插入OGNL表示式,則不需要單引號。struts標籤的include標籤官方文件:頁面內容:
    <li>
        include _include1.html 包含靜態英文檔案:
        <s:include value="_include1.html" ></s:include>
    </li>
    <li>
        include _include1.html 包含靜態中文檔案:
        <s:include value="_include2.html" ></s:include>
    </li>
    <li>
        include _include1.html 包含靜態中文檔案,說明%的用法:
        <s:set var="incPage" value="'/_include2.html'" />
        <s:include value="%{#incPage}" ></s:include>
    </li>
P.S:老版本的中文include bug已經修復,在此提到%{}的作用,即強制將字串定義為OGNL表示式進行傳遞 struts標籤的fieldError標籤
官方文件
頁面內容:
    <li>
        fieldError simple:<s:fielderror fieldName="fieldError.test" theme="simple"></s:fielderror>
    </li>
控制標籤 if、else if與else標籤 官方文件: if:
elseif:
頁面內容:
    <li>
        if elseif 和 else:
        age = <s:property value="#parameters.age" /><br/>
        <s:if test="#parameters.age[0] < 0">wrong age!</s:if>
        <s:elseif test="#parameters.age[0] <= 20" >too young!</s:elseif>
        <s:else>這才對嘛</s:else>
        <s:if test="#parameters.aaa == null">null</s:if>
    </li>
簡單easy,通俗易懂 P.S:不難發現這裡多次用到#parameters.age[0] 我們可以使用set標籤來優化語句

iterator標籤 官方文件:
頁面內容:
<li>
        遍歷集合:<br/>
        <s:iterator value="{1,2,3}">
            <s:property /> |
        </s:iterator>
    </li>

    <li>
        自定義變數:<br/>
        <s:iterator value="{'aaa','bbb','ccc'}" var="x" >
            <s:property value="#x.toUpperCase()" /> |
        </s:iterator>
    </li>

    <li>
        使用struts:<br/>
        <hr/>
        <s:iterator value="{'aaa','bbb','ccc'}" status="status">
            元素值:<s:property/> <br/>
            遍歷過的元素總數:<s:property value="#status.count" /> <br/>
            遍歷過的元素索引:<s:property value="#status.index" /> <br/>
            當前是偶數? <s:property value="#status.even"/><br/>
            當前是奇數? <s:property value="#status.odd"/><br/>
            是第一個元素嗎? <s:property value="#status.first" /><br/>
            是最後一個元素嗎? <s:property value="#status.last" /> <br/>
            <hr/>
        </s:iterator>
    </li>

    <li>
        遍歷Map:<br/>
        <s:iterator value="#{1:'a',2:'b',3:'c'}">
            <s:property  value="key" /> | <s:property value="value"/> <br/>
        </s:iterator>
    </li>
P.S:在遍歷Map時,需要在value里加入#符。

subset標籤 官方文件:
頁面內容:
     <s:subset source="myList" count="13" start="3">
        <s:iterator>
          <s:property />
        </s:iterator>
     </s:subset>
P.S:擷取部分集合內容,瞭解即可,過多不再闡述 UI標籤
開發中不常用,因為有不方便之處,與JavaScript結合不完美 總的 來說:
  1. 把所有的主體定義為simple
  2. fieldError特殊化處理
  3. 自己控制其他標籤的展現

BBS專案

注:專案開發環境由原來的struts-2.1.6換為struts-2.3.32,IDE版本升級為2017.1.1,在此不寫BBS專案開發程式碼,只寫流程,鞏固學習 專案開發事項 設計約定(編碼規定):
  1. 原則:能簡單就別複雜,簡單就是美;
  2. 庫名:專案名
  3. 表的命名:_Model名
  4. 欄位命名:保持與屬性名一致(儘量不要與資料庫命名衝突)
  5. 用層來劃分包:*.*.action: userAction studentAction   *.*.model(bean): User,Student. *.*.service(dao):userDAO,StudentDAO
  6. Action的命名:模組名+Action 駝峰命名規則
  7. JSP的命名:*-* 
  8. package的命名:action adminAction
開發過程: 1.建立介面模型
2.建立struts.xml
a).確定namespace
b).確定package
c).確定Action的名稱
d).確定result
e).將介面原型頁面進行修改,匹配現有配置
f).測試 3.建立資料庫(或實體類) 4.建立model層 5.建立service層(之後用hibernate完善) a).此時可以使用Junit進行測試 6.著手開發

宣告式異常處理

宣告丟擲一個異常交給struts處理,並進行指定操作。 如: 在此處我們先丟擲一個異常
然後在catch裡寫出throw語句
在***Action檔案內,將此異常接收,發往struts.xml檔案進行操作:
在struts.xml內,使用 exception-mapping接收異常並指定操作:
流程大概就是這樣,理解最重要。 使用宣告式異常的過程中,若需要給多個包配置異常,可使用global-exception-mapping進行配置,再使用global-result進行對映。其他包只需繼承該包即可 總結:
  1. 在Action中進行異常對映
  2. 在package中進行全域性異常對映
  3. 使用繼承共用異常對映
  4. struts2中異常處理由攔截器實現(觀察struts-default.xml)