關於學習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}
- *_*
- 接收引數的方法(一般用屬性和DomainModel來接收)
- 簡單的資料驗證 addFieldError
- 一般不使用struts2的ui標籤
- 訪問WEB元素
- Map型別(1、IoC 2、依賴struts2)
- 原始型別 (1、IoC 2、依賴struts2)
- 包含檔案配置(include標籤)
- 預設action處理
結果型別
result的type型別:- dispatcher
- redirect
- chain
- redirectAction
<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:- 常用的四中型別:
- dispatcher(預設)
- redirect
- chain
- redirecAction
- 全域性結果集
- global-results || extends(繼承另外一個包的結果集)
- 動態結果(瞭解即可)
- 在action中儲存一個屬性,儲存具體的結果location
- 傳遞引數
- 客戶端跳轉才需要傳遞
- ${}(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類:
- 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!"; } }
- 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 ; } }
- 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~"; } }
- 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; } }
- 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"; } }
- OGNL全稱 Object Gragh Navigation Language,物件圖導航語言
- 1.user.xxx 只有傳,才會構造(在頁面內只有傳了引數進去,值棧裡面才會有值) 當然可以直接new物件出來,預設變數值,效果一樣(domain model)。但是需要保持一個無參構造方法
<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標籤
要點集合:- 通用標籤:
- property
- set (預設為action scope 會將值放入request 和 ActionContext中 ; page,request,session,application)
- bean
- include(對中文檔案有支援問題,不建議使用,如需包含,改用jsp包含)
- param
- debug
- 控制標籤
- if、elseif、 else
- iterator(collections map enumeration iterator array,迴圈遍歷)
- subset(擷取集合一部分)
- UI標籤
- theme(simple xhtml:預設 、 css xhtml、 ajax )
- AJAX標籤
- 再補充
- $ # %的區別
- $用於 i18n 和 struts配置檔案
- #取得ActionContext的值
- %將原本的文字屬性解析為ognl,對於本來就是ongl的屬性不起作用(參考<s:property>和<s:include>)
<%--
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.xmltags.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結合不完美 總的 來說:
- 把所有的主體定義為simple
- fieldError特殊化處理
- 自己控制其他標籤的展現
BBS專案
注:專案開發環境由原來的struts-2.1.6換為struts-2.3.32,IDE版本升級為2017.1.1,在此不寫BBS專案開發程式碼,只寫流程,鞏固學習 專案開發事項 設計約定(編碼規定):- 原則:能簡單就別複雜,簡單就是美;
- 庫名:專案名
- 表的命名:_Model名
- 欄位命名:保持與屬性名一致(儘量不要與資料庫命名衝突)
- 用層來劃分包:*.*.action: userAction studentAction *.*.model(bean): User,Student. *.*.service(dao):userDAO,StudentDAO
- Action的命名:模組名+Action 駝峰命名規則
- JSP的命名:*-*
- package的命名:action adminAction
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進行對映。其他包只需繼承該包即可 總結:
- 在Action中進行異常對映
- 在package中進行全域性異常對映
- 使用繼承共用異常對映
- struts2中異常處理由攔截器實現(觀察struts-default.xml)