概述
-
什么是Struts2的框架
- Struts2是Struts1的下一代產(chǎn)品,是在 struts1和WebWork的技術(shù)基礎(chǔ)上進(jìn)行了合并的全新的Struts 2框架。
- 其全新的Struts 2的體系結(jié)構(gòu)與Struts 1的體系結(jié)構(gòu)差別巨大。
- Struts 2以WebWork為核心,采用攔截器的機制來處理用戶的請求,這樣的設(shè)計也使得業(yè)務(wù)邏輯控制器能夠與ServletAPI完全脫離開,所以Struts 2可以理解為WebWork的更新產(chǎn)品。
- 雖然從Struts 1到Struts 2有著太大的變化,但是相對于WebWork,Struts 2的變化很小。
-
Struts2是一個基于MVC設(shè)計模式的Web層框架
- MVC和JavaEE的三層結(jié)構(gòu)
- MVC設(shè)計模式:是由一些網(wǎng)站的開發(fā)人員提出來的
- JavaEE三層結(jié)構(gòu):SUN公司為EE開發(fā)劃分的結(jié)構(gòu)
- MVC和JavaEE的三層結(jié)構(gòu)
-
常見的Web層的框架
- Struts1
- Struts2
- Webwork
- SpringMVC
-
Web層框架的特點
- 都是一個特點,前端控制器模式
- 記住:前端控制器(核心的控制器)
- Struts2框架前端的控制器就是過濾器
Struts2快速入門的環(huán)境準(zhǔn)備
-
創(chuàng)建WEB項目,編寫JSP的頁面,編寫超鏈接,點擊超鏈接發(fā)送請求,請求服務(wù)器,讓服務(wù)器的方法去執(zhí)行?。?/p>
<h3>Struts2的入門程序</h3> <a href="${ pageContext.request.contextPath }/hello.action">Struts2入門程序</a> -
下載Struts2的開發(fā)包
- 官網(wǎng)地址 https://struts.apache.org/
-
解壓struts-2.3.24-all.zip包
- 解壓后會看到有包和一些文件,大家需要掌握包相關(guān)的信息
- apps -- Struts2框架提供了一些應(yīng)用
- libs -- Struts2框架開發(fā)的jar包
- docs -- Struts2框架開發(fā)文檔
- src -- Struts2框架源碼
- 解壓后會看到有包和一些文件,大家需要掌握包相關(guān)的信息
-
引入需要開發(fā)的jar包
- Struts2框架的開發(fā)jar包非常多,但是不是所有都是必須要引入的,有一些必須要導(dǎo)入的jar包,這些jar包可以從Struts2框架提供的應(yīng)用中找到。
- 大家可以打開apps目錄,然后找到struts2-blank.war應(yīng)用。war包和zip包的壓縮格式是一樣的,所以可以自己修改后綴名,解壓。
- 找到解壓后的應(yīng)用,打開WEB-INF/lib目錄下所以的jar包。復(fù)制到工程中,就可以了。
-
需要配置Struts2的前端控制器,注意:這一步是必須要做的操作,這是Struts2核心的控制器。
- Struts2的前端控制器就是一個過濾器,那么過濾器相關(guān)知識咱們都學(xué)習(xí)過,需要在web.xml中進(jìn)行配置。
- 前端控制器的類的路徑和名稱:org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
- 具體配置如下
<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>
6. 編寫Action類
Action類是動作類,是Struts2處理請求,封裝數(shù)據(jù),響應(yīng)頁面的核心控制器。需要自己編寫。
```
/**
* Stuts2框架都使用Action類處理用戶的請求
* @author Administrator
*/
public class HelloAction {
/**
* Action類中的方法簽名有要求的,必須這么做
* public 共有的
* 必須有返回值,必須String類型
* 方法名稱可以是任意的,但是不能有參數(shù)列表
*/
public String sayHello(){
// 編寫代碼 接收請求的參數(shù)
System.out.println("Hello Struts2!!");
return "ok";
}
```
7. 編寫Struts的配置文件
1. 配置文件名稱是struts.xml(名稱必須是struts.xml)
2. 在src下引入struts.xml配置文件(配置文件的路徑必須是在src的目錄下)
3. 配置如下
```
<?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="default" namespace="/" extends="struts-default">
<!-- 配置Action -->
<action name="hello" class="com.itheima.action.HelloAction"
method="sayHello">
<!-- 配置跳轉(zhuǎn)的頁面,路徑的寫法:在Struts2框架中,不管是轉(zhuǎn)發(fā)還是重定向,都不用寫項目名 -->
<result name="ok">/demo1/suc.jsp</result>
</action>
</action>
</package>
</struts>
```
###入門總結(jié)之Struts2的執(zhí)行流程
1. 執(zhí)行的流程
* 編寫的頁面,點擊超鏈接,請求提交到服務(wù)器端。
* 請求會先經(jīng)過Struts2的核心過濾器(StrutsPrepareAndExecuteFilter)
* 過濾器的功能是完成了一部分代碼功能
* 就是一系列的攔截器執(zhí)行了,進(jìn)行一些處理工作。
* 咱們可以在struts-default.xml配置文件中看到有很多的攔截器。可以通過斷點的方式來演示。
* 攔截器執(zhí)行完后,會根據(jù)struts.xml的配置文件找到請求路徑,找到具體的類,通過反射的方式讓方法執(zhí)行。
2. 總結(jié)
* JSP頁面-->StrutsPrepereAndExecuteFilter過濾器-->執(zhí)行一系列攔截器(完成了部分代碼)-->執(zhí)行到目標(biāo)Action-->返回字符串-->結(jié)果頁面(result)-->頁面跳轉(zhuǎn)

***
<br>
##Struts2框架配置文件加載的順序
0. 需要掌握
* 加載了哪些個配置文件(重點的)
* 配置文件的名稱是什么
* 配置文件的位置
* 配置文件的作用
1. Struts2框架的核心是StrutsPrepareAndExecuteFilter過濾器,該過濾器有兩個功能
* Prepare -- 預(yù)處理,加載核心的配置文件
* Execute -- 執(zhí)行,讓部分?jǐn)r截器執(zhí)行
2. StrutsPrepareAndExecuteFilter過濾器會加載哪些配置文件呢?
* 通過源代碼可以看到具體加載的配置文件和加載配置文件的順序
* init_DefaultProperties(); -- 加載org/apache/struts2/default.properties
* init_TraditionalXmlConfigurations(); -- 加載struts-default.xml,struts-plugin.xml,struts.xml
* init_LegacyStrutsProperties(); -- 加載自定義的struts.properties.
* init_CustomConfigurationProviders(); -- 加載用戶自定義配置提供者
* init_FilterInitParameters() ; -- 加載web.xml
3. 重點了解的配置文件
* default.properties -- 在org/apache/struts2/目錄下,代表的是配置的是Struts2的常量的值
* struts-default.xml -- 在Struts2的核心包下,代表的是Struts2核心功能的配置(Bean、攔截器、結(jié)果類型等)
* struts.xml -- 重點中的重點配置,代表WEB應(yīng)用的默認(rèn)配置,在工作中,基本就配置它就可以了?。。梢耘渲贸A浚? * web.xml -- 配置前端控制器(可以配置常量)
* 注意:
* 前3個配置文件是struts2框架的默認(rèn)配置文件,基本不用修改。
* 后3個配置文件可以允許自己修改struts2的常量。但是有一個特點:后加載的配置文件修改的常量的值,會覆蓋掉前面修改的常量的值。
4. 總結(jié)(重點掌握的配置文件)
* 先加載default.properties文件,在org/apache/struts2/default.properties文件,都是常量。
* 又加載struts-default.xml配置文件,在核心的jar包最下方,struts2框架的核心功能都是在該配置文件中配置的。
* 再加載struts.xml的配置文件,在src的目錄下,代表用戶自己配置的配置文件
* 最后加載web.xml的配置文件
* 后加載的配置文件會覆蓋掉之前加載的配置文件(在這些配置文件中可以配置常量)
5. 注意一個問題
* 哪些配置文件中可以配置常量?
* default.properties -- 默認(rèn)值,咱們是不能修改的?。? * struts.xml -- 可以配置,開發(fā)中基本上都在該配置文件中配置常量
* struts.properties -- 可以配置,基本不會在該配置文件中配置
* web.xml -- 可以配置,基本不會在該配置文件中配置
* 后加載的配置文件會覆蓋掉之前加載的配置??!
***
<br>
##入門總結(jié)之struts.xml配置文件的配置
1. <package>標(biāo)簽,如果要配置<Action>的標(biāo)簽,那么必須要先配置<package>標(biāo)簽,代表的包的概念
* 包含的屬性
* name -- 包的名稱,要求是唯一的,管理action配置
* extends -- 繼承,可以繼承其他的包,只要繼承了,那么該包就包含了其他包的功能,一般都是繼承struts-default
* namespace -- 名稱空間,一般與<action>標(biāo)簽中的name屬性共同決定訪問路徑(通俗話:怎么來訪問action),常見的配置如下
* namespace="/" -- 根名稱空間
* namespace="/aaa" -- 帶有名稱的名稱空間
* abstract -- 抽象的。這個屬性基本很少使用,值如果是true,那么編寫的包是被繼承的
2. <action>標(biāo)簽
* 代表配置action類,包含的屬性
* name -- 和<package>標(biāo)簽的namespace屬性一起來決定訪問路徑的
* class -- 配置Action類的全路徑(默認(rèn)值是ActionSupport類)
* method -- Action類中執(zhí)行的方法,如果不指定,默認(rèn)值是execute
3. <result>標(biāo)簽
* action類中方法執(zhí)行,返回的結(jié)果跳轉(zhuǎn)的頁面
* name -- 結(jié)果頁面邏輯視圖名稱
* type -- 結(jié)果類型(默認(rèn)值是轉(zhuǎn)發(fā),也可以設(shè)置其他的值)
***
<br>
##入門總結(jié)之Struts2配置常量
1. 可以在Struts2框架中的哪些配置文件中配置常量?
* struts.xml(必須要掌握,開發(fā)中基本上就在該配置文件中編寫常量)
* `<constant name="key" value="value"></constant>`

* web.xml
* 在StrutsPrepareAndExecuteFilter配置文件中配置初始化參數(shù)

* 注意:后加載的配置的文件的常量會覆蓋之前加載的常量??!
2. 需要大家了解的常量
* struts.i18n.encoding=UTF-8 -- 指定默認(rèn)編碼集,作用于HttpServletRequest的setCharacterEncoding方法
* struts.action.extension=action,, -- 該屬性指定需要Struts 2處理的請求后綴,該屬性的默認(rèn)值是action,即所有匹配*.action的請求都由Struts2處理。如果用戶需要指定多個請求后綴,則多個后綴之間以英文逗號(,)隔開
* struts.serve.static.browserCache=true -- 設(shè)置瀏覽器是否緩存靜態(tài)內(nèi)容,默認(rèn)值為true(生產(chǎn)環(huán)境下使用),開發(fā)階段最好關(guān)閉
* struts.configuration.xml.reload=false -- 當(dāng)struts的配置文件修改后,系統(tǒng)是否自動重新加載該文件,默認(rèn)值為false(生產(chǎn)環(huán)境下使用)
* struts.devMode = false -- 開發(fā)模式下使用,這樣可以打印出更詳細(xì)的錯誤信息
***
<br>
##入門總結(jié)之指定多個struts的配置文件(了解)
1. 在大部分應(yīng)用里,隨著應(yīng)用規(guī)模的增加,系統(tǒng)中Action的數(shù)量也會大量增加,導(dǎo)致struts.xml配置文件變得非常臃腫。
為了避免struts.xml文件過于龐大、臃腫,提高struts.xml文件的可讀性,我們可以將一個struts.xml配置文件分解成多個配置文件,然后在struts.xml文件中包含其他配置文件。
2. 可以在<package>標(biāo)簽中,使用<include>標(biāo)簽來引入其他的struts_xx.xml的配置文件。例如:
<struts>
<include file="struts-part1.xml"/>
<include file="struts-part2.xml"/>
</struts>
3. 注意注意注意(重要的事情說三遍):<include file="cn/itcast/demo2/struts-part1.xml"/>
***
<br>
##Action類的三種寫法
1. 配置文件學(xué)習(xí)完成,下面的重點是Action類的三種寫法
* Action類就是一個POJO類
* 什么是POJO類,POJO(Plain Ordinary Java Object)簡單的Java對象.簡單記:沒有繼承某個類,沒有實現(xiàn)接口,就是POJO的類。
```
/**
* 就是POJO類:沒有任何繼承和實現(xiàn)
*
* @author Administrator
*/
public class Demo1Action {
/**
* execute是默認(rèn)方法 return null; 不會進(jìn)行跳轉(zhuǎn)
*
* @return
*/
public String execute() {
System.out.println("Demo1Action就是POJO類...");
return null;
}
}
xml配置
<!-- POJO類的方式 -->
<action name="demo1Action" class="com.itheima.action1.Demo1Action" />
```
* Action類可以實現(xiàn)Action接口
* Action接口中定義了5個常量,5個常量的值對應(yīng)的是5個邏輯視圖跳轉(zhuǎn)頁面(跳轉(zhuǎn)的頁面還是需要自己來配置),還定義了一個方法,execute方法。
* 大家需要掌握5個邏輯視圖的常量
* SUCCESS -- 成功.
* INPUT -- 用于數(shù)據(jù)表單校驗.如果校驗失敗,跳轉(zhuǎn)INPUT視圖.
* LOGIN -- 登錄.
* ERROR -- 錯誤.
* NONE -- 頁面不轉(zhuǎn)向.
```
/**
* 實現(xiàn)Action的接口,Action是框提供的接口
*
* @author Administrator
*/
public class Demo2Action implements Action {
public String execute() throws Exception {
System.out.println("Demo2Action實現(xiàn)了Action的接口");
// return "success";
// return LOGIN;
// 表示頁面不跳轉(zhuǎn)
return NONE;
}
}
xml配置
<!-- 實現(xiàn)Action接口的方式 -->
<action name="demo2Action" class="com.itheima.action1.Demo2Action">
<result name="login">/demo1/suc.jsp</result>
</action>
```
* Action類可以去繼承ActionSupport類(開發(fā)中這種方式使用最多)
* 設(shè)置錯誤信息
```
/**
* 編寫Action類繼承ActionSupport類,ActionSupport類已經(jīng)實現(xiàn)了Action和一些其他接口
*
* @author Administrator
*/
public class Demo3Action extends ActionSupport {
private static final long serialVersionUID = 2183101963251216722L;
public String execute() throws Exception {
System.out.println("Demo3Action繼承了ActionSupport類...");
return NONE;
}
}
xml 配置
<!-- 繼承ActionSupport類的方式 -->
<action name="demo3Action" class="com.itheima.action1.Demo3Action" />
```
***
<br>
##Action的訪問
1. 通過<action>標(biāo)簽中的method屬性,訪問到Action中的具體的方法。
* 傳統(tǒng)的配置方式,配置更清晰更好理解!但是擴展需要修改配置文件等!
* 具體的實例如下:
* 頁面代碼
* `<a href="${pageContext.request.contextPath}/addBook.action">添加圖書</a>`
* `<a href="${pageContext.request.contextPath}/deleteBook.action">刪除圖書</a>`
* 配置文件的代碼
```
<package name="demo2" extends="struts-default" namespace="/">
<action name="addBook" class="cn.itcast.demo2.BookAction" method="add"></action>
<action name="deleteBook" class="cn.itcast.demo2.BookAction" method="delete"></action>
</package>
```
* Action的代碼
```
public String add(){
System.out.println("添加圖書");
return NONE;
}
public String delete(){
System.out.println("刪除圖書");
return NONE;
}
```
2. 通配符的訪問方式:(訪問的路徑和方法的名稱必須要有某種聯(lián)系.) 通配符就是 * 代表任意的字符
* 使用通配符的方式可以簡化配置文件的代碼編寫,而且擴展和維護(hù)比較容易。
* 具體實例如下:
* 頁面代碼
```
<a href="${pageContext.request.contextPath}/order_add.action">添加訂單</a>
<a href="${pageContext.request.contextPath}/order_delete.action">刪除訂單</a>
```
* 配置文件代碼
* `<action name="order_*" class="cn.itcast.demo2.OrderAction" method="{1}"></action>`
**{1} 表示 * 的取值**
* Action的代碼
```
public String add(){
System.out.println("添加訂單");
return NONE;
}
public String delete(){
System.out.println("刪除訂單");
return NONE;
}
```
* 具體理解:在JSP頁面發(fā)送請求,http://localhost/struts2_01/order_add.action,配置文件中的order_*可以匹配該請求,*就相當(dāng)于變成了add,method屬性的值使用{1}來代替,{1}就表示的是第一個*號的位置??!所以method的值就等于了add,那么就找到Action類中的add方法,那么add方法就執(zhí)行了!
3. 動態(tài)方法訪問的方式(有的開發(fā)中也會使用這種方式)
* 如果想完成動態(tài)方法訪問的方式,需要開啟一個常量,`struts.enable.DynamicMethodInvocation = false`,把值設(shè)置成`true`。
* 注意:不同的Struts2框架的版本,該常量的值不一定是true或者false,需要自己來看一下。如果是false,需要自己開啟。
* 在struts.xml中開啟該常量。
* `<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>`
* 具體代碼如下
* 頁面的代碼
```
<a href="${pageContext.request.contextPath}/product!add.action">添加商品</a>
<a href="${pageContext.request.contextPath}/product!delete.action">刪除商品</a>
```
路徑的寫法:`[packName]![methodName]`
* 配置文件代碼
* `<action name="product" class="cn.itcast.demo2.ProductAction"></action>`
* Action的類的代碼
```
public class ProductAction extends ActionSupport{
public String add(){
System.out.println("添加訂單");
return NONE;
}
public String delete(){
System.out.println("刪除訂單");
return NONE;
}
}
```
***
<br>
##在Struts2框架中使用Servlet的API
1. 在Action類中也可以獲取到Servlet一些常用的API
* 需求:提供JSP的表單頁面的數(shù)據(jù),在Action中使用Servlet的API接收到,然后保存到三個域?qū)ο笾?,最后再顯示到JSP的頁面上。
* 提供JSP注冊的頁面,演示下面這三種方式
```
<h3>注冊頁面</h3>
<form action="${ pageContext.request.contextPath }/xxx.action" method="post">
姓名:<input type="text" name="username" /><br/>
密碼:<input type="password" name="password" /><br/>
<input type="submit" value="注冊" />
</form>
```
2. 完全解耦合的方式
* 如果使用該種方式,Struts2框架中提供了一個類,ActionContext類,該類中提供一些方法,通過方法獲取Servlet的API
* 一些常用的方法如下
* `static ActionContext getContext()` -- 獲取ActionContext對象實例
* `java.util.Map<java.lang.String,java.lang.Object> getParameters()` -- 獲取請求參數(shù),相當(dāng)于`request.getParameterMap()`;
* `java.util.Map<java.lang.String,java.lang.Object> getSession()` -- 獲取的代表session域的Map集合,就相當(dāng)于操作session域。
* `java.util.Map<java.lang.String,java.lang.Object> getApplication()` -- 獲取代表application域的Map集合
* `void put(java.lang.String key, java.lang.Object value) ` -- 注意:向request域中存入值。
```
/**
* 完全解耦合的方式,使用Servlet的API
* @author Administrator
*/
public class Demo1Action extends ActionSupport{
private static final long serialVersionUID = -7255855724015241518L;
public String execute() throws Exception {
// 完全接耦合的方式
ActionContext context = ActionContext.getContext();
// 獲取到請求的參數(shù),封裝所有請求的參數(shù)
Map<String, Object> map = context.getParameters();
// 遍歷獲取數(shù)據(jù)
Set<String> keys = map.keySet();
for (String key : keys) {
// 通過key,來獲取到值
String [] vals = (String[]) map.get(key);
System.out.println(key+" : "+Arrays.toString(vals));
}
// 如果向request對象中存入值
context.put("msg", "小東東");
// 獲取其他map集合
context.getSession().put("msg", "小蒼");
context.getApplication().put("msg", "小澤");
return SUCCESS;
}
}
jsp:
<h3>使用EL表達(dá)式獲取值</h3>
${ sessionScope.msg }
${ requestScope.msg }
${ applicationScope.msg }
```
3. 使用原生Servlet的API的方式
* Struts2框架提供了一個類,`ServletActionContext`,該類中提供了一些靜態(tài)的方法
* 具體的方法如下
* `getPageContext()`
* `getRequest()`
* `getResponse()`
* `getServletContext()`
/**
- 原生的API
- @author Administrator
*/
public class Demo2Action extends ActionSupport{
private static final long serialVersionUID = -864657857993072618L;
public String execute() throws Exception {
// 獲取到request對象
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("msg", "小東東");
request.getSession().setAttribute("msg", "美美");
ServletActionContext.getServletContext().setAttribute("msg", "小鳳");
HttpServletResponse response = ServletActionContext.getResponse();
// 使用輸出流,輸出內(nèi)容
return SUCCESS;
}
}
jsp:
<h3>使用EL表達(dá)式獲取值</h3>
${ sessionScope.msg }
${ requestScope.msg }
${ applicationScope.msg }
***
<br>
##結(jié)果頁面的跳轉(zhuǎn)
1. 結(jié)果頁面存在兩種方式
* 全局結(jié)果頁面
> 條件:如果<package>包中的一些action都返回success,并且返回的頁面都是同一個JSP頁面,這樣就可以配置全局的結(jié)果頁面。
> 全局結(jié)果頁面針對的當(dāng)前的包中的所有的Action,但是如果局部還有結(jié)果頁面,會優(yōu)先局部的。使用的標(biāo)簽是
```
<global-results>
<result>/demo3/suc.jsp</result>
</global-results>
```
* 局部結(jié)果頁面
```
<result>/demo3/suc.jsp</result>
```
2. 結(jié)果頁面的類型
* 結(jié)果頁面使用<result>標(biāo)簽進(jìn)行配置,包含兩個屬性
* name -- 邏輯視圖的名稱
* type -- 跳轉(zhuǎn)的類型,值一些,需要掌握一些常用的類型。
常見的結(jié)果類型去struts-default.xml中查找。
* `dispatcher` -- 轉(zhuǎn)發(fā).type的**默認(rèn)值**。Action--->JSP
* `redirect` -- 重定向。 Action--->JSP
* `chain` -- 多個action之間跳轉(zhuǎn).從一個Action轉(zhuǎn)發(fā)到另一個Action。 Action---Action
* `redirectAction` -- 多個action之間跳轉(zhuǎn)。從一個Action重定向到另一個Action. Action---Action
* `stream` -- 文件下載時候使用的
代碼演示:
```
public class Demo3Action extends ActionSupport{
private static final long serialVersionUID = 7455069037223451306L;
public String save(){
System.out.println("save...");
return SUCCESS;
}
// 訪問: demo3Action_update
public String update(){
System.out.println("update...");
return NONE;
}
}
struts.xml
<!-- 演示重定向到Action -->
<action name="demo3Action_*" class="com.itheima.demo1.Demo3Action" method="{1}">
<result name="success" type="redirectAction">demo3Action_update</result>
</action>
```
這里的`<result name="success" type="redirectAction">demo3Action_update</result>`,重定向路徑名稱直接是:demo3Action_update,不用寫其它路徑信息。

##Struts2框架的數(shù)據(jù)封裝
1. 為什么要使用數(shù)據(jù)的封裝呢?
* 作為MVC框架,必須要負(fù)責(zé)解析HTTP請求參數(shù),并將其封裝到Model對象中
* 封裝數(shù)據(jù)為開發(fā)提供了很多方便
* Struts2框架提供了很強大的數(shù)據(jù)封裝的功能,不再需要使用Servlet的API完成手動封裝了!!
2. Struts2中提供了兩類數(shù)據(jù)封裝的方式?
* 第一種方式:屬性驅(qū)動
> 提供對應(yīng)屬性的set方法進(jìn)行數(shù)據(jù)的封裝。
* 表單的哪些屬性需要封裝數(shù)據(jù),那么在對應(yīng)的Action類中提供該屬性的set方法即可。
* 表單中的數(shù)據(jù)提交,最終找到Action類中的setXxx的方法,最后賦值給全局變量。
* 注意0:Struts2的框架采用的攔截器完成數(shù)據(jù)的封裝。
* 注意1:這種方式不是特別好:因為屬性特別多,提供特別多的set方法,而且還需要手動將數(shù)據(jù)存入到對象中.
* 注意2:這種情況下,Action類就相當(dāng)于一個JavaBean,就沒有體現(xiàn)出MVC的思想,Action類又封裝數(shù)據(jù),又接收請求處理,耦合性較高。
```
/**
* 屬性驅(qū)動
* @author Administrator
*/
public class Regist1Action extends ActionSupport{
private static final long serialVersionUID = -966487869258031548L;
private String username;
private String password;
private Integer age;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(Integer age) {
this.age = age;
}
public String execute() throws Exception {
System.out.println(username+" "+password+" "+age);
return NONE;
}
}
jsp:
<h3>屬性驅(qū)動的方式</h3>
<form action="${ pageContext.request.contextPath }/regist1.action" method="post">
姓名:<input type="text" name="username" /><br/>
密碼:<input type="password" name="password" /><br/>
年齡:<input type="password" name="age" /><br/>
<input type="submit" value="注冊" />
</form>
```
**在頁面上,使用OGNL表達(dá)式進(jìn)行數(shù)據(jù)封裝。**
* 在頁面中使用OGNL表達(dá)式進(jìn)行數(shù)據(jù)的封裝,就可以直接把屬性封裝到某一個JavaBean的對象中。
* 在頁面中定義一個JavaBean,并且提供set方法:例如:private User user;
* 頁面中的編寫發(fā)生了變化,需要使用OGNL的方式,表單中的寫法:<input type="text" name="user.username">
* 注意:只提供一個set方法還不夠,必須還需要提供user屬性的get和set方法?。?!
> 先調(diào)用get方法,判斷一下是否有user對象的實例對象,如果沒有,調(diào)用set方法把攔截器創(chuàng)建的對象注入進(jìn)來,
```
/**
* 屬性驅(qū)動方式,把數(shù)據(jù)封裝到JavaBean的對象中
* @author Administrator
*/
public class Regist2Action extends ActionSupport{
private static final long serialVersionUID = 6556880331550390473L;
// 注意二:屬性驅(qū)動的方式,現(xiàn)在,要提供是get和set方法
private User user;
public User getUser() {
System.out.println("getUser...");
return user;
}
public void setUser(User user) {
System.out.println("setUser...");
this.user = user;
}
public String execute() throws Exception {
System.out.println(user);
return NONE;
}
}
jsp:
<h3>屬性驅(qū)動的方式(把數(shù)據(jù)封裝到JavaBean的對象中)</h3>
<!-- 注意一:頁面的編寫規(guī)則,發(fā)生了變化,使用的OGNL表達(dá)式的寫法 -->
<form action="${ pageContext.request.contextPath }/regist2.action" method="post">
姓名:<input type="text" name="user.username" /><br/>
密碼:<input type="password" name="user.password" /><br/>
年齡:<input type="password" name="user.age" /><br/>
<input type="submit" value="注冊" />
</form>
javabean:
public class User {
private String username;
private String password;
private Integer age;
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", age=" + age + "]";
}
}
```
* 第二種方式:模型驅(qū)動
> 使用模型驅(qū)動的方式,也可以把表單中的數(shù)據(jù)直接封裝到一個JavaBean的對象中,并且表單的寫法和之前的寫法沒有區(qū)別!
> 編寫的頁面不需要任何變化,正常編寫name屬性的值
> 模型驅(qū)動的編寫步驟:
* 手動實例化JavaBean,即:private User user = new User();
* 必須實現(xiàn)ModelDriven<T>接口,實現(xiàn)getModel()的方法,在getModel()方法中返回user即可??!
```
/**
* 模型驅(qū)動的方式
* 實現(xiàn)ModelDriven接口
* 必須要手動實例化對象(需要自己new好)
* @author Administrator
*/
public class Regist3Action extends ActionSupport implements ModelDriven<User>{
private static final long serialVersionUID = 6556880331550390473L;
// 必須要手動實例化
private User user = new User();
// 獲取模型對象
public User getModel() {
return user;
}
public String execute() throws Exception {
System.out.println(user);
return NONE;
}
}
jsp:
<h3>模型驅(qū)動方式</h3>
<form action="${ pageContext.request.contextPath }/regist3.action" method="post">
姓名:<input type="text" name="username" /><br/>
密碼:<input type="password" name="password" /><br/>
年齡:<input type="password" name="age" /><br/>
<input type="submit" value="注冊" />
</form>
```
***
<br>
##Struts2把數(shù)據(jù)封裝到集合中
1. 封裝復(fù)雜類型的參數(shù)(集合類型 Collection 、Map接口等)
2. 需求:頁面中有可能想批量添加一些數(shù)據(jù),那么現(xiàn)在就可以使用上述的技術(shù)了。把數(shù)據(jù)封裝到集合中
3. 把數(shù)據(jù)封裝到Collection中
* 因為Collection接口都會有下標(biāo)值,所有頁面的寫法會有一些區(qū)別,注意:
> <input type="text" name="products[0].name" />
* 在Action中的寫法,需要提供products的集合,并且提供get和set方法。
```
/**
* 屬性驅(qū)動的方式,把數(shù)據(jù)封裝到List集合中
* @author Administrator
*/
public class Regist4Action extends ActionSupport{
private static final long serialVersionUID = 6556880331550390473L;
private List<User> list;
public List<User> getList() {
return list;
}
public void setList(List<User> list) {
this.list = list;
}
public String execute() throws Exception {
for (User user : list) {
System.out.println(user);
}
return NONE;
}
}
jsp:
<h3>向List集合封裝數(shù)據(jù)(默認(rèn)情況下,采用是屬性驅(qū)動的方式)</h3>
<!-- 后臺:List<User> list -->
<form action="${ pageContext.request.contextPath }/regist4.action" method="post">
姓名:<input type="text" name="list[0].username" /><br/>
密碼:<input type="password" name="list[0].password" /><br/>
年齡:<input type="password" name="list[0].age" /><br/>
姓名:<input type="text" name="list[1].username" /><br/>
密碼:<input type="password" name="list[1].password" /><br/>
年齡:<input type="password" name="list[1].age" /><br/>
<input type="submit" value="注冊" />
</form>
```
4. 把數(shù)據(jù)封裝到Map中
* Map集合是鍵值對的形式,頁面的寫法
> <input type="text" name="map['one'].name" />
* Action中提供map集合,并且提供get和set方法
```
/**
* 屬性驅(qū)動的方式,把數(shù)據(jù)封裝到map集合中
* @author Administrator
*/
public class Regist5Action extends ActionSupport{
private static final long serialVersionUID = 6556880331550390473L;
private Map<String, User> map;
public Map<String, User> getMap() {
return map;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
public String execute() throws Exception {
System.out.println(map);
return NONE;
}
}
jsp:
<h3>向Map集合封裝數(shù)據(jù)(默認(rèn)情況下,采用是屬性驅(qū)動的方式)</h3>
<form action="${ pageContext.request.contextPath }/regist5.action" method="post">
姓名:<input type="text" name="map['one'].username" /><br/>
密碼:<input type="password" name="map['one'].password" /><br/>
年齡:<input type="password" name="map['one'].age" /><br/>
姓名:<input type="text" name="map['two'].username" /><br/>
密碼:<input type="password" name="map['two'].password" /><br/>
年齡:<input type="password" name="map['two'].age" /><br/>
<input type="submit" value="注冊" />
</form>
```
***
<br>
##Struts2的攔截器技術(shù)
1. 攔截器的概述
* 攔截器就是AOP(Aspect-Oriented Programming)的一種實現(xiàn)。(AOP是指用于在某個方法或字段被訪問之前,進(jìn)行攔截然后在之前或之后加入某些操作。)
* 過濾器:過濾從客服端發(fā)送到服務(wù)器端請求的
* 攔截器:攔截對目標(biāo)Action中的某些方法進(jìn)行攔截
* 攔截器不能攔截JSP
* 攔截到Action中某些方法
2. 攔截器和過濾器的區(qū)別
1)攔截器是基于JAVA反射機制的,而過濾器是基于函數(shù)回調(diào)的
2)過濾器依賴于Servlet容器,而攔截器不依賴于Servlet容器
3)攔截器只能對Action請求起作用(Action中的方法),而過濾器可以對幾乎所有的請求起作用(CSS JSP JS)
* 攔截器 采用 責(zé)任鏈 模式
> 在責(zé)任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈
> 責(zé)任鏈每一個節(jié)點,都可以繼續(xù)調(diào)用下一個節(jié)點,也可以阻止流程繼續(xù)執(zhí)行
* 在struts2 中可以定義很多個攔截器,將多個攔截器按照特定順序 組成攔截器棧 (順序調(diào)用 棧中的每一個攔截器 )
3. Struts2的核心是攔截器,看一下Struts2的運行流程

##自定義攔截器和配置
###方式一:
/**
編寫簡單的攔截器
-
@author Administrator
*/
public class DemoInterceptor extends AbstractInterceptor{private static final long serialVersionUID = 4360482836123790624L;
/**
-
intercept用來進(jìn)行攔截的
*/
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("Action方法執(zhí)行之前...");
// 執(zhí)行下一個攔截器
String result = invocation.invoke();System.out.println("Action方法執(zhí)行之后...");
return result;
}
-
}
Struts.xml
<package name="demo3" extends="struts-default" namespace="/">
<!-- 定義了攔截器 第一種方式 -->
<interceptors>
<interceptor name="DemoInterceptor" class="com.itheima.interceptor.DemoInterceptor"/>
</interceptors>
<action name="userAction" class="com.itheima.demo3.UserAction">
<!-- 只要是引用自己的攔截器,默認(rèn)棧的攔截器就不執(zhí)行了,必須要手動引入默認(rèn)棧 -->
<interceptor-ref name="DemoInterceptor"/>
<interceptor-ref name="defaultStack"/>
</action>
</package>
Action:
public class UserAction extends ActionSupport{
public String execute() throws Exception {
System.out.println("我是Action,我正常執(zhí)行...");
return NONE;
}
}
###方式二:
攔截器的另一種方式配置
<package name="demo3" extends="struts-default" namespace="/">
<!-- 第二種方式:定義攔截器棧 -->
<interceptors>
<interceptor name="DemoInterceptor" class="com.itheima.interceptor.DemoInterceptor"/>
<!-- 定義攔截器棧 -->
<interceptor-stack name="myStack">
<interceptor-ref name="DemoInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="userAction" class="com.itheima.demo3.UserAction">
<!-- 引入攔截器棧就OK -->
<interceptor-ref name="myStack"/>
</action>
</package>
***
<br>
##OGNL表達(dá)式概述(了解)
1. OGNL是Object Graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫
* 所謂對象圖,即以任意一個對象為根,通過OGNL可以訪問與這個對象關(guān)聯(lián)的其它對象
* 通過它簡單一致的表達(dá)式語法,可以存取對象的任意屬性,調(diào)用對象的方法,遍歷整個對象的結(jié)構(gòu)圖,實現(xiàn)字段類型轉(zhuǎn)化等功能。它使用相同的表達(dá)式去存取對象的屬性
2. Struts2框架使用OGNL作為默認(rèn)的表達(dá)式語言
* OGNL是一種比EL強大很多倍的語言
* xwork提供 OGNL表達(dá)式
* ognl-3.0.5.jar
3. OGNL 提供五大類功能
* 支持對象方法調(diào)用
* 支持類靜態(tài)的方法調(diào)用和值訪問
* 訪問OGNL上下文(OGNL context)和ActionContext
* 支持賦值操作和表達(dá)式串聯(lián)
* 操作集合對象
4. 測試的代碼
```
// 訪問對象的方法
@Test
public void run1() throws OgnlException{
OgnlContext context = new OgnlContext();
// 獲取對象的方法
Object obj = Ognl.getValue("'helloworld'.length()", context, context.getRoot());
System.out.println(obj);
}
// 獲取OGNL上下文件的對象
@Test
public void run3() throws OgnlException{
OgnlContext context = new OgnlContext();
context.put("name", "美美");
// 獲取對象的方法
Object obj = Ognl.getValue("#name", context, context.getRoot());
System.out.println(obj);
}
// 從root棧獲取值
@Test
public void demo3() throws OgnlException{
OgnlContext context = new OgnlContext();
Customer c = new Customer();
c.setCust_name("haha");
context.setRoot(c);
String name = (String) Ognl.getValue("cust_name", context, context.getRoot());
System.out.println(name);
}
```
***
<br>
##在Struts2框架中使用OGNL表達(dá)式
1. Struts2引入了OGNL表達(dá)式,主要是在JSP頁面中獲取值棧中的值
2. 具體在Struts2中怎么使用呢?如下步驟
* 需要先引入Struts2的標(biāo)簽庫
`<%@ taglib prefix="s" uri="/struts-tags" %>`
* 使用Struts2提供的標(biāo)簽中的標(biāo)簽
`<s:property value="OGNL表達(dá)式"/>`
3. 在JSP頁面使用OGNL表達(dá)式
* 訪問對象方法
`<s:property value="'hello'.length()"/>`
###值棧的概述
1. 問題一:什么是值棧?
* 值棧就相當(dāng)于Struts2框架的數(shù)據(jù)的中轉(zhuǎn)站,向值棧存入一些數(shù)據(jù)。從值棧中獲取到數(shù)據(jù)。
* ValueStack 是 struts2 提供一個接口,實現(xiàn)類 OgnlValueStack ---- 值棧對象 (OGNL是從值棧中獲取數(shù)據(jù)的 )
* Action是多例的,有一起請求,創(chuàng)建Action實例,創(chuàng)建一個ActionContext對象,代表的是Action的上下文對象,還會創(chuàng)建一個ValueStack對象。
* 每個Action實例都有一個ValueStack對象 (一個請求 對應(yīng) 一個ValueStack對象 )
* 在其中保存當(dāng)前Action 對象和其他相關(guān)對象
* Struts 框架把 ValueStack 對象保存在名為 “struts.valueStack” 的請求屬性中,request中 (值棧對象 是 request一個屬性)
* `ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");`
###值棧的內(nèi)部結(jié)構(gòu)
2. 問題二 : 值棧的內(nèi)部結(jié)構(gòu) ?
* 值棧由兩部分組成
> root -- Struts把動作和相關(guān)對象壓入 ObjectStack 中--List
> context -- Struts把各種各樣的映射關(guān)系(一些 Map 類型的對象) 壓入 ContextMap 中
* Struts會默認(rèn)把下面這些映射壓入ContextMap(context)中
* 注意:request代表的是Map集合的key值,value的值其實也是一個Map集合。
> parameters: 該 Map 中包含當(dāng)前請求的請求參數(shù) ?name=xxx&password=123
> request: 該 Map 中包含當(dāng)前 request 對象中的所有屬性
> session: 該 Map 中包含當(dāng)前 session 對象中的所有屬性
> application:該 Map 中包含當(dāng)前 application 對象中的所有屬性
> attr: 該 Map 按如下順序來檢索某個屬性: request, session, application
* ValueStack中 存在root屬性 (CompoundRoot) 、 context 屬性 (OgnlContext )
> CompoundRoot 就是ArrayList
> OgnlContext 就是 Map
* context 對應(yīng)Map 引入 root對象
> context中還存在 request、 session、application、 attr、 parameters 對象引用
> OGNL表達(dá)式訪問值棧中的數(shù)據(jù)
* 訪問root中數(shù)據(jù)時 不需要 #
* 訪問 request、 session、application、 attr、 parameters 對象數(shù)據(jù) 必須寫 #
> 操作值棧 默認(rèn)指 操作 root 元素

***
###值棧的創(chuàng)建和ActionContext對象的關(guān)系
3. 問題三 : 值棧對象的創(chuàng)建,ValueStack 和 ActionContext 是什么關(guān)系?
* 值棧對象是請求時創(chuàng)建的
* ActionContext是綁定到當(dāng)前的線程上,那么在每個攔截器或者Action中獲取到的ActionContext是同一個。
* ActionContext中存在一個Map集合,該Map集合和ValueStack的context是同一個地址。
* ActionContext中可以獲取到ValueStack的引用,以后再開發(fā),使用ActionContext來獲取到值棧對象
技術(shù)分析之獲取到值棧的對象
4. 問題四 : 如何獲得值棧對象
* 獲得值棧對象 有三種方法
* `ValueStack vs1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");`
* `ValueStack vs2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);`
* `ValueStack vs3 = ActionContext.getContext().getValueStack();`
技術(shù)分析之向值棧中保存數(shù)據(jù)
5. 問題五: 向值棧保存數(shù)據(jù) (主要針對root棧)
* valueStack.push(Object obj);
* push方法的底層調(diào)用root對象的push方法(把元素添加到0位置**棧頂**)
* valueStack.set(String key, Object obj);
* 源碼獲取map集合(map有可能是已經(jīng)存在的,有可能是新創(chuàng)建的),把map集合push到**棧頂**,再把數(shù)據(jù)存入到map集合中。
```
public String execute() throws Exception {
// 獲取到值棧對象,先獲取到ActionContext對象
ValueStack vs = ActionContext.getContext().getValueStack();
System.out.println(vs);
// 棧頂壓的小風(fēng) 字符串
vs.push("小鳳");
// 棧頂:
vs.set("msg", "美美");
vs.set("info", "小蒼");
return NONE;
}
```
在jsp中 通過 <s:debug /> 查看值棧的內(nèi)容
***
<br>
###從值棧中獲取值
6. 問題六: 在JSP中獲取值棧的數(shù)據(jù)
* 總結(jié)幾個小問題:
> 訪問root中數(shù)據(jù) 不需要#
> 訪問context其它對象數(shù)據(jù) 加 #
> 如果向root中存入對象的話,優(yōu)先使用push方法。
> 如果向root中存入集合的話,優(yōu)先要使用set方法。
* 在OgnlContext中獲取數(shù)據(jù)
> 在Action中向域?qū)ο笾写嫒胫? > request:<s:property value="#request.username"/>
> session:<s:property value="#session.username"/>
> application:<s:property value="#application.username"/>
> attr:<s:property value="#attr.username"/>
> parameters:<s:property value="#parameters.cid"/>
* 6.1 代碼如下
```
<!--
// vs.push("美美");
// 獲取到棧頂?shù)闹? <s:property value="[0].top"/>
-->
<!--
// 棧頂是map集合,通過key獲取值
vs.set("msg", "小鳳");
<s:property value="[0].top.msg"/>
-->
<!--
vs.push(user);
// 棧頂放user對象
<s:property value="[0].top.username"/>
<s:property value="[0].top.password"/>
// [0].top 關(guān)鍵字是可以省略的 findValue()
<s:property value="username"/>
-->
<!--
vs.set("user", user);
<s:property value="[0].top.user.username"/>
<s:property value="[0].top.user.password"/>
// 省略關(guān)鍵字
<s:property value="user.username"/>
-->
<!--
// 在ValueStack1Action提供了成員的屬性
private User user = new User("小澤","456");
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
User user = new User("小蒼","123");
vs.set("user", user);
// 從棧頂開始查找,找user的屬性,顯示名稱 返回的小蒼
<s:property value="user.username"/>
// [1].top獲取ValueStack1Action [1].top.user返回user對象 [1].top.user.username獲取對象的屬性名稱
<s:property value="[1].top.user.username"/>
-->
<!--
棧頂是list集合
vs.push(ulist);
<s:property value="[0].top[0].username"/>
<s:property value="[0].top[1].username"/>
-->
<!--
vs.set("ulist", ulist);
<s:property value="ulist[0].username"/>
-->
<!-- 迭代的標(biāo)簽
屬性
* value 要迭代的集合,需要從值棧中獲取
* var 迭代過程中,遍歷的對象
* var編寫上,把迭代產(chǎn)生的對象默認(rèn)壓入到context棧中,從context棧取值,加#號
* var不編寫,默認(rèn)把迭代產(chǎn)生的對象壓入到root棧中
for(User user:ulist){}
// 編寫var的屬性
<s:iterator value="ulist" var="u">
<s:property value="#u.username"/>
<s:property value="#u.password"/>
</s:iterator>
// 沒有編寫var關(guān)鍵字
<s:iterator value="ulist">
<s:property value="username"/>
<s:property value="password"/>
</s:iterator>
-->
<!-- 從context棧中獲取值,加#號
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("msg", "美美");
request.getSession().setAttribute("msg", "小風(fēng)");
<s:property value="#request.msg"/>
<s:property value="#session.msg"/>
<s:property value="#parameters.id"/>
<s:property value="#attr.msg"/>
-->
<!-- 在JSP頁面上,查看值棧的內(nèi)部結(jié)構(gòu) -->
<s:debug></s:debug>
```
***
<br>
###EL表達(dá)式也會獲取到值棧中的數(shù)據(jù)
7. 問題七:為什么EL也能訪問值棧中的數(shù)據(jù)?
```
<!--
在JSP頁面上可以使用EL和JSTL標(biāo)簽庫來取值
使用裝飾者模式,連接池 全站編碼
getAttribute()增強了
-->
<c:forEach items="${ ulist }" var="user">
${ user.username } -- ${ user.password }
</c:forEach>
```
* StrutsPreparedAndExecuteFilter的doFilter代碼中 request = prepare.wrapRequest(request);
> 對Request對象進(jìn)行了包裝 ,StrutsRequestWrapper
> 增強了request的 getAttribute
```
Object attribute = super.getAttribute(s);
if (attribute == null) {
attribute = stack.findValue(s);
}
```
> 訪問request范圍的數(shù)據(jù)時,如果數(shù)據(jù)找不到,去值棧中找
> request對象 具備訪問值棧數(shù)據(jù)的能力 (查找root的數(shù)據(jù))