struts2總結
1.為什么學習Struts框架
a.Struts框架好處
struts2是主流的開發技術,大多數公司在使用,struts把相關的servlet組件的各種操作都進行了相應的封裝,這樣就可以輕松地完成復雜的操作。Struts2是基于MVC模式開發的,MVC結構是一個優秀的設計思想,可以提高程序結構的靈活性,便于日后的維護和擴展。
--簡化開發工作量,提高開發效率
--采用了優秀的設計思想(MVC)
b.Struts框架作用
struts充當控制器,接收請求,分發處理,
c.如何學習Struts框架
先重點掌握框架使用,原理性有余力可以多了解些。
注:我們目前使用的Struts版本為struts2 2.1.8。(2.1.6版本bug較多)
d.學校struts要掌握的重點:
valuestack
action
result
攔截器
標記庫
2.MVC模式在JavaWeb中的應用
*a.什么是MVC
是一種軟件架構的思想,將一個軟件的組成部分劃分成三種不同類型的 模塊,分別是:
模型(用于封裝業務邏輯,包括業務數據和業務處理邏輯,由JavaBean組件 (比如實體類,DAO,Service)實現),
視圖(負責顯示界面與用戶交互,處理表示邏輯,一種是展示數據,另一種 是接受數據,由jsp組件實現),
控制器(用于控制流程,協調模型與視圖,由Servlet、Filter組件(比如之 前的ActionServlet)實現)。
使用mvc的最主要的原因是為了實現模型的復用:即模型只需要開發 一次, 模型不關心處理的結果如何展現,模型將結果交給不同的視 圖,由視圖來展 現這些數據;另外,可以利用不同的視圖來訪問 同一個模型。
MVC:基于面向對象的一種常見的體系架構
Model:
封裝數據以及對數據操作的方法(相當于飯店中做菜的材料(數據)和做菜的廚師(方法)
一個流程用一個方法
View:
封裝的是用戶界面,接收用戶請求,負責輸入和輸出(和用戶打交道)(相當于服務員)
Controller:
封裝的是業務流程,控制業務流程(相當于飯店中的管理者)(如果程序比較簡單,可以把Controller寫在Model里面)
b.為什么使用MVC
企業程序更強調結構靈活,易于擴展和升級。所以廣泛選用MVC模式開發。
*c.JavaWeb中如何實現MVC結構
JavaWeb技術主要有:Servlet,JSP,JavaBean,Filter,Listener等
V部分:JSP組件 (cart.jsp)
M部分:JavaBean組件(entity,dao)
C部分:Servlet,Filter組件 (ShoppingCartServlet)
*d.MVC處理流程
--首先瀏覽器發出請求
--請求進入C控制器
--C控制器調用M模型組件處理
--M模型組件處理后返回處理結果
-- C控制器調用V視圖組件生成響應信息
*e.Struts框架的MVC實現
原有ShoppingCart程序,雖然符合MVC結構,但不適合大型項目開發,因為請求一多,ShoppingCartServlet會出現大量的if..else... ,可以進行以下方式的改造:
為ShoppingCartServlet配置一個XML文件,該文件定義不同請求和不同Action組件的對應關系,將原有if..else..分支處理用Action組件封裝。
**f.Struts框架的處理流程
--瀏覽器發出struts請求
--請求進入struts控制器
--struts控制器解析xml配置文件(xml定義了請求和Action對應關系)
--控制器根據不同請求,調用不同的Action
--Action調用DAO處理請求,之后返回結果
--控制器根據結果調用視圖組件為用戶響應
struts總結
3.了解Struts歷史
Struts框架分成Struts1和Struts2.Struts2和Struts1沒有必然聯系。Struts2是以WebWork框架核心(xwork)為基礎構建起來
最早出現的 Struts1是一個非常著名的框架,它實現了MVC模式。Struts1簡單小巧,其中最成熟的版本是Struts1.2。之后出現了WebWork框架,其實現技術比Struts1先進,但影響力不如Struts1。在框架技術不斷發展過程中,有人在WebWork核心XWork的基礎上包裝了 Struts1(算是兩種框架的整合),由此,結合了Struts1的影響力和WebWork的先進技術,Struts 2誕生了。所以說,Struts2不是Struts1的升級,它更像是WebWork的升級版本。
*4.Struts基本使用
a.引入struts2核心開發包(5個)
(1).struts2-core-2.1.8.1.jar
Struts2核心包,是Struts框架的“外衣。
(2).xwork-core-2.1.6.jar
Struts2核心包,是WebWork內核。
(3).ognl-2.7.3.jar
用來支持ognl表達式的,類似于EL表達式,功能比EL表達式強大的
多。
(4).freemarker-2.3.15.jar
freemarker是比jsp更簡單好用,功能更加強大的表現層技術,用來 替代jsp的。在Struts2中提倡使用freemarker模板,但實際項目中使 用jsp也很多。
(5).commons-fileupload-1.2.1.jar
用于實現文件上傳功能的jar包。
b.在web.xml中配置下struts控制器
c.根據請求編寫Action,JSP
d.在struts.xml定義Action和請求對應關系
5.HelloWord入門實例
welcome.action-->Struts控制器-->struts.xml-->WelcomeAction
-->welcome.jsp
(Action請求可以是/welcome.action或者/welcome兩種寫法)
7、struts.xml基本配置
struts.xml放在src根目錄下。
a) <struts>
根元素,可以包含多個<package>元素
b)<package>
<package name="***" namespace="/默認" extends="struts-default">
元素主要用于將Action分組定義。name屬性用于指定包名;extends一般指 定為struts-default。struts-default這個包在struts-default.xml中定義,其中 包含了struts2運行所需的必要組件。
添加namespace屬性的主要作用是避免在大項目中出現的命名沖突問題。
注意:
http://localhost:8080/struts01/netctoss/welcome
<package name="demo1" namespace="/netctosss"
extends="struts-default">
<action name="welcome" class="action.WelcomeAction">
<result name="success">/WEB-INF/jsp/Welcome.jsp</result>
</action>
namespace屬性默認為“/”(即從應用名后的第一個"/"開始到最后一個“/”結束)。
eg:對于http://localhost:8080/struts01/netctoss/cost/showList.action
其中的namespace屬性值應為”/netctoss/cost”。
eg:
如果不加namespace="/netctoss"那么
http://localhost:8080/struts01/netctoss/list.action便不會得到正確的匹配
一個<package>元素可以包含多個<action>..</action>主要用于定義請求
和Action組件的對應關系。name屬性指定請求名;class屬性指定Action的包
名.類名
c)<action>
<action name="***" method="execute默認" class="***">
一個<action>元素可以包含多個<result>...<result>主要用于定義視圖響 應。name屬性與Action.execute()返回值一致。
其中
(1).method屬性可以省略,如果省略則默認調用Action中的execute()方法
(2.)如果不指定class屬性,Struts會默認調用框架底層的ActionSupport
類處理(見下面:)。框架會默認為該<action>添加一個class,作用是
轉發到對應
的<result name="success">中指定的頁面。當有nameform請求發
來時,struts不會調 用action(找不到)而是直接調用result中name屬
性為success對應的頁面。
注:name=”success”可以省略
<action name="nameform">
<!--<result name=”success”>/WEB-INF/jsp/Welcome.jsp</result>-->
<result>/WEB-INF/jsp/Welcome.jsp</result>
</action>
struts總結
--------ActionSupport類:---------
public String execute(){
return “success”;
}
d)<result>
<result name="success默認" type="dispatcher默認">
e:注意:
1.對于package、action、result,如果使用默認屬性值,則可以省略屬性名。
eg:<result name=”sucess” type=”dispatcher”>****</sesult>
如果使用默認屬性值:則可以寫成:
<result >****</sesult>
對于重復使用的result可以寫成全局的result
<!-- 定義全局result -->
<global-results>
<!-- 錯誤處理 -->
<result name="error">/WEB-INF/jsp/error.html</result>
<!-- 攔截器登錄處理 -->
<result name="login" type="redirectAction">showLogin</result>
</global-results>
2.page包中的各組件的放置順序:
具體見struts-2.1.7dtd
package (result-types——> interceptors——>default-interceptor-ref——>
default-action-ref——> default-class-ref——> global-results——>
global-exception-mappings——> action*)>
攔截器配置放在global-results之前
3.當項目業務處理較多時如果將所有的配置都寫在struts.xml一個文件中將很不方便,所以可以將不同的處理模塊分別寫在不同的xml文件中,最后在struts.xml中加載和模塊配置文件,過程如下:
struts.xml:
<struts>
<!-- 加載各模塊配置文件 -->
<include file="struts-cost.xml"/>
<include file="struts-account.xml"/>
...........
<package name="netctoss-default" extends="json-default">
<!-- 追加共通的Action,Interceptor組件 -->
攔截器、全局results....
</package>
</struts>
struts-cost.xml:
<!-- 資費模塊配置文件 -->
<struts>
<package name="cost" extends="netctoss-default">
........
</package>
</struts>
struts-account.xml:
<!-- 資費模塊配置文件 -->
<struts>
<package name="account" extends="netctoss-default">
........
</package>
</struts>
.............................
瀏覽器訪問地址:
http://localhost:8080/應用名/namespace/action
http://localhost:8080/struts03_2/list/costList.action?page=4
以上的namespace默認為’’/’,本例為list
8、如何解決用戶繞過ActionServlet,直接訪問jsp文件的問題。
將所有jsp文件拷貝到WEB-INF目錄下。因為WEB-INF目錄中的內容不能直接訪問,但能轉發過來。
當然也可以用之前的session技術(太麻煩)。
對于struts中WebRoot文件夾下的.jsp頁面仍可以直接輸入地址訪問。
9、Struts2提供的方便之處:
a.數據的自動的封裝
根據頁面組件的name屬性,自動封裝到Action中對應的name屬性中。
在Jsp頁面<input name=”name” type=”text”/>
在action中會自動給屬性private String name 賦值。但是注意要有name對應的getter和setter方法。
b.數據的自動的傳遞
Action中的屬性在jsp頁面可以直接用EL表達式拿到
eg:
Action中屬性private String name;
在jsp頁面上可以直接${name}的到對應的值
按理說,EL表達式取數據的對象只有pageContext,request,session,application四類,不應該從Action對象中取name屬性值,但 struts框架重新包裝了request,getAttribute()方法被改寫(request的其他方法沒被改寫),如果從request對象中調用getAttribute()方法沒獲取到值(null),那么會自動從action中取,這樣,struts中EL表達式就可以直接獲取到 Action屬性中的值了。
上面介紹的采用了包裝模式:(相當于手機加殼案例和線程池案例(其中的close方法特殊)。參考如下:
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
//重寫
public Object getAttribute(String key){
//尋找原request信息
Object obj = super.getAttribute(key);
if(obj == null){
//尋找valuestack信息
obj = valuestack.findValue(key);
//Ognl.getValue(key,context,root);
} struts總結
return obj;
}
}
${name} -->request.getAttribute("name")
-->Ognl.getValue("name",context,root)
10.項目示例(資費列表顯示)
a.顯示默認主頁面
index.action-->struts控制器-->index.html
b.資費列表顯示
feeList.action-->struts控制器
-->FeeListAction-->FeeDAO-->COST
-->fee_list.jsp
1)編寫COST對應的Entity實體類
2)編寫DAO,DBUtil
--引入oracle驅動包
3)編寫Action
4)配置Action
5)編寫JSP
--引入jstl.jar,standard.jar
11.OGNL技術的使用
1)什么是OGNL
OGNL是Object-Graphic Navigation Language(對象圖導航語言)的縮寫,
是一種功能強大的表達式語言(與EL表達式類似)。OGNL可以讓我們用非常
簡單的表達式訪問對象層(訪問對象的數據和方法),它用一個獨立的lib形
式出現(封裝于ognl.jar工具包中),方便我們使用或者構建自己的框架。
OGNL區別于EL表達式,OGNL可以在.java文件中通過getValue()方法訪問對
象,而EL表達式不可以在.java文件中使用;EL表達式在.jsp文件中可以直接
輸出數據,而OGNL不可以,OGNL在.jsp文件中只能當做struts標簽中的屬性
值,通過struts標簽訪問數據。
2)OGNL基本原理
OGNL工具主要由3部分構成,具體如下
a.OGNL引擎
負責解析ONGL表達式,定位數據
b.Root根存儲區(Object類型)
負責存儲要訪問的目標對象(一個)。
c.Context變量存儲區(Map類型)
負責存放多個要訪問的目標對象。
*3)OGNL基本語法
a.訪問Root區域對象基本語法
Ognl.getValue("OGNL表達式", "Map存儲器內容","根存儲器內容");
Ognl.getValue("OGNL表達式", "根存儲器內容");
--訪問簡單數據:"屬性" 例如"name"
int id = (Integer)Ognl.getValue("id", foo);
String name = (String)Ognl.getValue("name", foo);
--訪問數組或List集合:"屬性[0]"
例如"arr[1]"
String arrVal = (String)Ognl.getValue("arr[1]", foo);
String listVal = (String)Ognl.getValue("list[2]", foo);
--訪問map集合元素:"屬性['key']"或"屬性.key"
例如"map.c"或"map[‘two’]"
注:”map[‘a’]”不可用(‘a’結果為整數)
String mapVal = (String)Ognl.getValue("map.b", foo);
String mapval2 = (String)Ognl.getValue("map['user.name']",foo);
--訪問方法:"屬性值.方法()",
例如"list.size()"
System.out.println(Ognl.getValue("map['two'].toUpperCase()", foo));
System.out.println(Ognl.getValue("list.size()", foo));
--訪問構造方法:"new 包名.構造方法"
例如"new java.util.Date()"
Object obj =
Ognl.getValue("new org.tarena.entity.Foo('TOM')", foo);
--訪問靜態成員:"@包名.類名@成員"
Ognl.getValue("@java.lang.Math@abs(-1)", foo);
Ognl.getValue("@java.lang.Math@PI", foo);
--創建List對象:"{元素1,元素2}"
Object obj1 = Ognl.getValue("{'struts','hibernate','spring'}",foo);
--創建Map對象:
"#{key1:value1,key2:value2}"
Object obj2 = Ognl.getValue("#{1:'tom',2:'jack',3:'rose'}", foo);
public static void main(String[] args) throws Exception {
Foo foo = new Foo();
foo.setId(100);
foo.setName("張三");
foo.setArr(new String[] { "A", "B", "C" });
foo.setList(Arrays.asList("芙蓉", "鳳姐", "春哥"));
Map<String, String> map = new HashMap<String, String>();
map.put("one", "Java");
map.put("two", "JavaWeb");
map.put("three", "Struts2");
map.put("user.name", "Scott");
foo.setMap(map);
//測試OGNL訪問foo目標對象
//Ognl.getValue("OGNL表達式", "Map存儲器內容","根存儲器內容");
//Ognl.getValue(expression, root);
Map context = new HashMap();
//訪問root基本的屬性值
int id = (Integer)Ognl.getValue("id", foo);
System.out.println(id);
String name = (String)Ognl.getValue("name", foo);
System.out.println(name);
System.out.println(Ognl.getValue("user.name", foo));
//訪問root數組和集合元素
String arrVal = (String)Ognl.getValue("arr[1]", foo);
System.out.println(arrVal);
String listVal = (String)Ognl.getValue("list[2]", foo);
System.out.println(listVal);
//訪問root對象的map集合元素
String mapVal = (String)Ognl.getValue("map.b", foo);
System.out.println(mapVal);
String mapval2 = (String)Ognl.getValue("map['user.name']",foo);
System.out.println(mapval2);
//支持運算符
System.out.println(Ognl.getValue("id+1",foo));
//System.out.println((Integer)Ognl.getValue("id",foo)+1);
System.out.println(Ognl.getValue("\"姓名:\"+name", foo));
System.out.println(Ognl.getValue("id > 200",foo));
//方法調用
System.out.println(Ognl.getValue("map['two'].toUpperCase()", foo));
System.out.println(Ognl.getValue("list.size()", foo));
//構造方法的調用
Object obj = Ognl.getValue("new org.tarena.entity.Foo('TOM')", foo);
System.out.println(obj);
System.out.println(Ognl.getValue("name", obj));
//靜態方法和常量的調用
System.out.println(Ognl.getValue("@java.lang.Math@abs(-1)", foo));
System.out.println(Ognl.getValue("@java.lang.Math@PI", foo));
//創建一個list集合
Object obj1 = Ognl.getValue("{'struts','hibernate','spring'}",foo);
System.out.println(obj1.getClass().getName());
//創建一個map集合
Object obj2 = Ognl.getValue("#{1:'tom',2:'jack',3:'rose'}", foo);
System.out.println(obj2.getClass().getName());
}
12、OGNL技術在Struts的使用
在Struts2中有一個ValueStack(本質就是存放數據的集合)數據對象,該對象存儲了請求相關的所有數據信息。例如request,session,application,action等.
Struts2采用OGNL工具對ValueStack進行操作。
1)xwork對OGNL進行了部分改造
在xwork中將原來OGNL標準結構中的Root存儲區改造成了一個棧結構
(CompoundRoot)以前只能存放一個對象,現在進行了擴容,可以存放
多個對象
當利用"屬性"表達式訪問時,優先去棧頂對象尋找,沒有再去次棧頂尋找。
2)ValueStack結構(OgnlValueStack)
ValueStack就是一個容器(Map集合)(存放東西的不是Tomcat容器)
3)Struts2標簽的使用
在JSP中,利用Struts2標簽顯示數據,需要為標簽指定OGNL表達式,標簽利用表達式定位ValueStack中的數據,進行相應操作。
a) debug標簽:顯示valuestack狀態
<s:debug></s:debug>
b) iterator標簽:循環集合元素
value屬性:指定ognl
var屬性:指定循環變量,會被存放到ValueStack的context區域。
status屬性:指定循環狀態變量,會被存放到ValueStack的context區域.該
變量有count屬性表示一共循環了多少個元素。
index屬性表示當前循環元素的索引。
<s:iterator value="new int[totalPages]" status="i">
<s:if test="#i.count==page">
<a class="current_page"href="costList.action?page=${i.count }">
<s:property value="#i.count"/>
</a>
</s:if>
<s:else>
<a href="costList.action?page=${i.count }">
<s:property value="#i.count"/>
</a>
</s:else>
</s:iterator>
c) if...else...標簽:判斷分支
test屬性:指定ognl判斷表達式
<s:if test="page < totalPages" >
<a href="costList?page=${page+1 }">下一頁</a>
</s:if>
<s:else>
下一頁
</s:else>
d) property標簽:顯示數據
value屬性:指定ognl表達式
<s:iterator value="costList" var="cost">
<tr><td><s:property value="#cost.id"/></td></tr>
</s:iterator>
e) date標簽:將date數據格式化輸出
name屬性:指定ognl表達式
format屬性:指定日期模板
<td>
<s:date name="#cost.createTime" format="yyyy-MM-dd HH:mm:ss"/>
</td>
13、Action組件的相關使用
1)Action組件的原理
--客戶發出action請求給struts控制器
--前端控制器截獲請求并根據配置信息調用對應的action
--struts控制器會創建valuestack對象
--struts控制器根據請求創建Action對象并將Action壓入valuestack的root
棧頂(Action線性安全,不用考慮并發問題,struts會為每一個請求 創
建一個Action對象,區別于Servlet)
--struts控制器調用一系列攔截器做一些輔助性的工作(一般都是
valuestack的操作),如通過parmaeterInterceptor將表單中的數據賦值
給action中的對象;還有將請求相關request,session對象放入到
valuestack的context區域,同時將ValueStack對象存入到request中。
存儲的key為”struts.valueStack”。(這樣,當一次請求結束后,容器
會銷毀request,request銷毀了,其中的valuestack對象也就銷毀了)
--控制器調用Action,執行業務方法處理(根據輸入算輸出并返回結果)
--控制器根據Action返回值調用result視圖組件處理。
--請求處理完畢,銷毀valuestack和Action對象。
一次請求,一個valuestack和action對象
2)Struts中寫Action過程:
定輸入、定輸出、根據輸入算輸出、返回結果
*3)Action中如何使用session,request
(1).利用ActionContext和ServletActionContext工具類(不推薦)
a.ActionContext返回是Struts框架封裝成Map之后的
request,session,application.
eg:
//將用戶信息寫入Session
ActionContext ac = ActionContext.getContext();
Map<String,Object> session = ac.getSession();
session.put("user", adminCode);
Map<String,Object> application = ac.getApplication();
Map<String,Object> request = (Map)ac.get("request");
b.ServletActionContext返回的是Servlet中使用的request, session,
application原有類型。
(使用建議:存值取值建議用Map結構API,如果有特殊需求再采用
Servlet原有類型(如獲取客戶端IP))
eg:
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = httpRequest.getSession();
HttpServletResponse response = ServletActionContext.getResponse();
ServletContext application = ServletActionContext.getServletContext()
ActionContext工具類中的方法getSession()如何將HttpSession對
象封裝成Map對象的說明:
Map----impements--->AbstructMap---extends-->SessionMap(重
寫了Map中的put和get方法)
----如何將原有HttpSession封裝成Map------
public class SessionMap extends AbstractMap{
private HttpSession session;
public SessionMap(HttpSession session){
this.session = session;
}
public void put(Object key,Object val){
session.setAttribute(key,val);
}
public Object get(Object key){
session.getAttribute(key);
}
}
不推薦使用 ActionContext訪問Session的方式,因為這種方式的“侵入性”較強。ActionContext是Struts2的API, 如果使用其他框架代替目前的Struts2框架,而我們實際項目中的Action的數量非常龐大,每個類都修改的話,會非常麻煩。
(2).利用Aware接口方式注入(推薦)
將Action實現Aware接口,由框架底層將對象注入給Action中相應的變量
當action實例化時候,struts框架就會自動將session對象(在valuestack中)賦值給實現了SessionAware接口的類中的變量(就是注入)。
(即session,ruquest,response,application)
RequestAware 對應的是Map結構的Request
SessionAware 對應的是Map結構的Session
ApplicationAware 對應的是Map結構的Application
ServletRequestAware對應的HttpServletRequest
ServletResponseAware對應的HttpServeltResponse
ServletContextAware對應的ServletContext
eg:
public class LoginAction implements SessionAware{
protected Map<String,Object> session;
...............
public void setSession(Map<String, Object> session) {
this.session = session;
}
public String execute(){
if(("admin".equals(adminCode)) && ("1234".equals(passWord))){
session.put("user", adminCode);
return "success";
}else{
return "error";
}
}
}
(3)、使用工具類(ActionContext、ServletActionContext)還是
Aware接口?
Action組件內部用Aware接口、Action組件之外(比如后面的攔截器)用ActionContext、ServletActionContext
4)Action屬性注入
作用:在創建Action對象時指定屬性值
在<action>配置中為Action組件的屬性指定一個初始值,該值在創建
Action對象時注入。可以將一些變化的參數值,利用該方法指定。例如
pageSize,管理員用戶名,dir存儲路徑等。
在<action>配置中,為Action對象的屬性指定初始值。使用格式如下:
<action name="feeList" class="org.action.FeeListAction">
<!--創建Action對象時給屬性指定一個值-->
<param name="屬性名">屬性值</param>
<result></result>
</action>
eg:
public class ListAction{
private Stirng param1;
private String param2;
.....
public String getParam1(){
return param1;
}
..... struts總結
public setParam1(Stirng param1){
this.param1 = param1;
}
.......
}
struts.xml:
<action name="feeList" class="org.action.FeeListAction">
<param name="param1">admin</param>
<param name="param2">1234</param>
<result>........</result>
</action>
5)Action通配符配置
Action通配符可以將多個具有相同屬性的Action整合為一個Action。
在<action>配置時,name請求名可以使用若干個*符號。然后再其他屬
性中通過使用{1},{2}引用第一個*號,第二個*號所代表的字符串。
<!-- 通配符配置 -->
<action name="*_*" method="{2}"class="com.tarena.action.{1}">
<result name="success">/index.jsp</result>
</action>
eg:
CostAction.java:
public class CostAction {
//屬性定義--省略
public String add(){
System.out.println("資費添加操作");
return "success";
}
public String update(){
System.out.println("資費更新操作");
return "success";
}
}
struts.xml:
<action name="cost_*" method="{1}"
class="com.tarena.action.CostAction">
<result name="success">/index.jsp</result>
</action>
14、Result組件的相關使用
1)Result組件原理
a.Result組件的作用
Result組件負責生成響應視圖內容。將ValueStack中的數據做不同形式
的輸出。
Struts2框架提供了多種不同的Result組件類型,用于做不同形式響應。
例如json數據響應,流數據響應,jsp響應等。
b.Result組件相關配置
--Result聲明定義
<package>
<result-types>
<result-type name="result類型" class="Result組件實現類"/>
</result-types>
</package>
--Result使用定義
<action>
<result name="標示符" type="result類型"></result>
</action>
c.編寫一個轉發功能的Result組件
定義的類類必須實現Result接口(Result組件),并且將約定的execute方法實現在該方法中編寫了生成響應輸出的邏輯代碼。
public class MyDispatcherResult implements Result{
private String location;
//setter和getter方法
................
//約定方法
public void execute(){
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response =ServletActionContext.getResponse();
RequestDispatcher rd = request.getRequestDispatcher(location);
rd.forward(request,response);
}
}
*2)掌握經常使用的Result組件
a. JSP視圖
--dispatcher(默認):以轉發方式調用JSP
--redirect:以重定向方式調用JSP
<result name="" type="dispatcher(默認)/redirect">
jsp頁面
</result>
b.Action視圖
--chain:以轉發方式調用下一個Action
--redirectAction:以重定向方式調用下一個Action
c:
stream:以字節流方式響應,Action中的InputStream類型的屬性以
字節流方式輸出。
eg:由服務器端生成驗證碼圖片并以流的方式發送給瀏覽器。
在struts.xml端的配置如下:
<!--驗證碼處理 -->
<action name="image" class="org.tarena.netctoss.action.ImageAction">
<result type="stream" name="success">
<param name="inputName">imageStream</param>
</result>
<result name="fail">/WEB-INF/jsp/login.jsp</result>
</action>
注:其中的<param name="inputName">imageStream</param>區別于Action
屬性注入,其作用為:給result對象指定參數值
d:
json:以json字符串響應,將Action中指定的屬性拼成一個json字符串輸
出。
注:
其中,a、b、c中的result組件在’struts-defaulg”包下,d中的result組件
在”json-default”,struts-core.jar中的默認包
<package name="struts-default" abstract="true">如下:
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain" class="*****"/>
<result-type name="dispatcher" class="*****"/>
<result-type name="freemarker" class="*****"/>
<result-type name="httpheader" class="*****"/>
<result-type name="redirect" class="*****"/>
<result-type name="redirectAction" class="*****"/>
<result-type name="stream" class="class="*****"/>
<result-type name="velocity" class="class="*****"/>
<result-type name="xslt" class="*****"/>
<result-type name="plainText" class="*****"/>
</result-types>
</package>
相同命名空間調用:
<result type="redirectAction">
請求名
</result>
跨命名空間調用
<result type="redirectAction">
<param name="actionName">請求名</param>
<param name="namespace">/</param>
</result>
======資費刪除流程=====
fee_delete.action-->FeeDeleteAction-->FeeDAO
-->FeeListAction-->fee_list.jsp
============練習=============
完成課上項目任務。
擴展任務:完成資費的添加,更新處理,查看,啟用等。
16、json Result組件
主要負責將Action的屬性以json字符串格式輸出。
json Result的使用步驟:
a.引入struts2-json-plugin.jar
b.將<package>元素的extends屬性改為繼承"json-default"(json-default包
的范圍比原struts-default包大,所以更改后仍可以使用以前包中的組件)
c.<result>使用配置
//只返回Action中一個屬性值
<result type="json">
<param name="root">屬性名</param>
</result>
//返回Action中多個屬性值
<result type="json">
<param name="includeProperties">
屬性名1,屬性名2,屬性名3
</param>
</result>
//返回Action中所有屬性值
<result type="json">
</result>
因為使用name屬性默認值,所以name=”success”可以省略。
17、Struts2表單標簽
1.什么是標簽
struts標簽封裝了界面顯示邏輯,用于簡化JSP。
jstl標簽用來代替jsp頁面中的java代碼,struts標簽不但可以代替jsp頁面中的
java代碼,還可以代替jsp頁面中html標記
2.struts常用標簽:
textfield,password,hidden,textarea
radio,checkboxlist,select
checkbox,form
a) textfield
以輸入框方式顯示屬性值.
name屬性指定ognl表達式,表示輸入框中的值
<input type="text" class="readonly" name=”cost.id” value=${cost.id}
readonly />
相當于:
<s:textfield name="cost.id" cssClass="readonly" readonly="true">
</s:textfield>
注:規定輸入字段為只讀(內容不可被更改)。在html中input標簽readonly屬性
直接寫,在struts標簽中要寫成:readonly=”true”。類似的的還有disabled要寫成
disabled=”true”
b) textarea
以文本域方式顯示屬性值.
name屬性指定ognl表達式,表示文本域中的值
<textarea class="width300 height70">${cost.descr}</textarea>
<s:textarea name="cost.descr" cssClass="width300 height70"></s:textarea>
c)password:
以文本框方式顯示密碼
name屬性指定ognl表達式,,表示輸入框中的值
注:默認情況下,文本框內不會顯示密碼,如果要顯示密碼則需要設置showPassword屬性。
密碼:<s:password name="password" showPassword="true"></s:password>
d) radio:
以一組單選框方式顯示,并可以根據屬性值決定哪個選項選中。
list屬性指定集合ognl,用于生成若干radio選項。
name屬性指定ognl,用于決定哪個raido選中。
<s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'計時'}">
</s:radio>
e)checkbox:
以單選框方式顯示選中狀態
name屬性指定ognl表達式,name屬性值為true時,單選框默認會被選中
eg:
婚姻狀況:<s:checkbox name="marry""></s:checkbox>已婚
f)checkboxlist:
以復選框方式顯示選中狀態
name屬性指定ognl表達式,用來決定哪些復選框默認被選中。
list屬性為map<key,value>集合
listKey屬性為map集合中的key
listValue屬性為map集合中的value,為在界面顯示的內容
參考struts05_1中的form.jsp
eg1:
愛好:<s:checkboxlist name="chk" list="#session.favors" listKey="id"
listValue="name"></s:checkboxlist>
eg2:
<s:checkboxlist name=”” list=”costList’” listKey="id"
listValue="name"></s:checkboxlist>
g)select:
以下拉列表的方式顯示數據:
name屬性指定ognl表達式,用來決定哪個option默認被選中。
eg1:
<s:select name="status" cssClass="select_search"
list="#{‘0’:’暫停’,'1':'開通','2':'暫停','3':'刪除'}">
</s:select>
相當于
<select class="select_search" >
<option value="2">全部</option>
<option value="1">開通</option>
<option value="0">暫停</option>
<option value="-1">刪除</option>
</select>
eg2:
<s:select list=”costList” name=”” listKey=”id” listValue=”name”></s:select>
注:對于上面的struts標簽中的name屬性,不但可以指定ognl表達式獲取相關的數據,還可以將action中的屬性名設置為name的屬性值,這樣就可以實現表單數據的自動提交。
h)form:
theme屬性:表示主題,可選項有xhtml和simple;如果不寫,默認為xhtml 。
theme屬性如果為默認值(xhtml),那么在使用struts表單標
簽時候會自動添加樣式(自動添加tr、tr,讓顯示更美觀),
這樣就會破壞當前頁面的布局和樣式。
如果在用struts表單標簽時,沒有用struts中的form標簽而是
使用html中的form標簽,那么在使用struts表單標簽時候也
會自動添加樣式
<s:form action="update" namespace="/" theme="simple">
.............
</s:form>
通用屬性(所有標記都具備的屬性):
label
labelpostion
required
tooltip
tooltipIconPath
cssClass(html中的class)
cssStyle(html中的style)
name
value
18、攔截器組件
2)什么是攔截器:
一般利用攔截器封裝一些通用性處理功能,便于重復利用。例如請求參
數給action屬性賦值、日志記錄、權限檢查、文件上傳、事務處理等。攔截
器通過配置方式調用,因此使用方法比較靈活,便于維護或擴展。
Struts2框架提供了大量的攔截器組件,如果不能滿足開發者需要,可以
進行自定義。
eg:
<interceptor name="params"
class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
其中ParametersInterceptor類用于在創建Action對象時將表單中的參數
賦值給Action中的相關屬性。
具體見:
ReferencedLibraries/struts2-core-2.1.8.jar/struts-default.xml
/<interceptors>標記
2)攔截器完整工作流程:
a.客戶發送一個Action請求,請求交給控制器
b.控制器創建一個ValueStack、Action對象,并將Action壓入ValueStack棧頂
c.控制器調用ActionInvocation組件執行請求處理
d.ActionInvocation組件調用請求相關的攔截器組件--前部分處理.
e.然后再調用Action組件業務方法
f.然后再調用Result組件處理方法
g.最后執行攔截器組件--后部分處理.
h.將響應信息輸出,為客戶端響應
3)攔截器作用
攔截器可以在Action和Result組件調用之前執行,也可以在Action和Result
組件之后執行。
4)自定義攔截器
a.編寫攔截器組件實現類(兩種方法):
1.編寫攔截器組件實現類實現Interceptor接口的interceptor方法。
public class MyInterceptor implements Interceptor{
public String intercept (ActionInvocation in){
//攔截器的前期處理邏輯
in.invoke();//執行Action和Result
//in.invokeActionOnly();//僅執行Action
//攔截器的后期處理邏輯
}
}
eg:
public class CheckLoginInterceptor implements Interceptor{
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation in) throws Exception {
System.out.println("(前期處理)自定義攔截器被調用。。。");
ActionContext ac = ActionContext.getContext();
//ActionContext ac = in.getInvocationContext();
Map<String,Object> session = ac.getSession();
//登錄檢查
if(session.get("user") == null){
//為登錄
return "login";//進入登錄頁面
}
in.invoke();//執行Action、DAO和Result--->jsp
System.out.println("(后期處理)自定義攔截器被調用。。。");
return null;//invoke()方法執行之后,后面的返回值都無效(即不會返回),因為執行in.invoke(),也即執行Action和Result調用Jsp后,返回的值沒有任何意義。
}
}
2.所有攔截器組件實現類也可以通過繼承AbstractInterceptor類并實現
inteceptor方法。
eg:
public class Interceptor2 extends AbstractInterceptor {
public String intercept(ActionInvocation in) throws Exception {
System.out.println("(前期處理)自定義攔截器被調用。。。");
// ActionContext ac = ActionContext.getContext();
ActionContext ac = in.getInvocationContext();
Map<String, Object> session = ac.getSession();
// 登錄檢查
if (session.get("user") == null) {
// 未登錄
return "login";// 進入登錄頁面
}
in.invoke();// 執行Action和Result
System.out.println("(后期處理)自定義攔截器被調用。。。");
return null;//invoke()方法執行之后,后面的返回值都無效(即不會返回),寫著只是好看一點
}
}
注意:當執行了invoke()方法后,invoke()方法后面的返回值無效(即不會返回),寫著只是好看一點。如果沒有執行invoke(),返回值有效。控制器會根據該返回值調用一個Result生成響應信息
b.注冊攔截器(將攔截器注冊給struts2框架)
(1).方法一:
<package>
<interceptors>
<interceptor name="攔截器名"class="攔截器實現類"/>
<interceptor name="" class=""/>
</interceptors>
</package>
eg:
<!-- 定義攔截器 -->
<interceptors>
<interceptor name="checklogin"
class="org.tarena.netctoss.interceptor.CheckLoginInterceptor2"/>
</interceptors>
<!-- 使用攔截器 -->
<action name="costDelete" class=”org.tarena.netctos.CostDeleteAction”>
<interceptor-ref name="checklogin"/>
<interceptor-ref name="defaultStack"/>//調用默認攔截器棧中的攔截器
<result name=”cuccess”>/WEB-INF/jsp/cost/cost_delete.jsp</result>
</action>
struts總結
(1).方法二
創建攔截器棧(<interceptor-stack name="loginStack">),將多個攔截器放在攔截器棧中,這樣就可以在需要的地方直接引入攔截器棧就行了(<interceptor-ref name="loginStack"/>)
eg:
<!-- 定義攔截器 -->
<interceptors>
<interceptor name="checklogin"
class="org.tarena.netctoss.interceptor.CheckLoginInterceptor2"/>
<interceptor-stack name="loginStack">
<interceptor-ref name="checklogin"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 使用攔截器 -->
<action name="costDelete" class=”org.tarena.netctos.CostDeleteAction”>
<interceptor-ref name="loginStack"/>
<result name=”cuccess”>/WEB-INF/jsp/cost/cost_delete.jsp</result>
</action>
方法二中的定義攔截器的方法實際上是先定義一個checklogin攔截器,然后定義一個攔截器棧(用于存放多個攔截器),把定義的checklogin攔截器和struts默認的攔截器放在攔截器棧里面。在使用攔截器時候,直接引入攔截器棧就可以了(相當于引入了checklogin攔截器和struts默認攔截器)。
c.使用攔截器組件
(1).為所有Action指定默認使用配置,這樣,攔截器便對該包中的所有Action
起攔截作用
<package ******>
<default-interceptor-ref name="攔截器名"/>
</package>
--(2).為某一個Action指定使用配置
<action>
<interceptor-ref name="攔截器名"/>
</action>
eg:
<action name="showAddCost">
<interceptor-ref name="loginStack"/>
<interceptor-ref name="defaultStack"/>
<result>/WEB-INF/jsp/cost/cost_add.jsp</result>
</action>
注意:如果為某個Action指定了攔截器配置,不會再調用指定的默認攔截器.所以一般會加上<interceptor-ref name="defaultStack"/>。
5)fileUpload內置攔截器的使用
a)fileUpload攔截器功能
用于實現文件上傳功能。
fileUpload攔截器處理流程:
客戶端發送上傳文件請求
首先,struts控制器收到請求后調用fileUpload攔截器,fileUpload攔截器調用commons-file-upload.jar上傳組件對請求提交
的表單信息進行解析。
然后將解析后的文件保存到服務器臨時目錄下,并將臨時目錄下的文件
對象賦值給Action的File屬性。
執行Action,在Actin業務中,需要做文件復制,將臨時文件轉移到目標
目錄中。
當Action和Result調用完畢后,清除臨時目錄下的文件,因此在Actin業務中,需要做文件復制,將臨時文件轉移到目標目錄中。
b)上傳示例
注意事項:
--需要引入commons-io.jar)
--form表單method必須為post;enctype必須為multipart/form-data
--Action組件按下面規則接收表單信息
<input type="file" name="xxx">
Action屬性如下:
private File xxx;// 臨時目錄下的文件
private String xxxFileName;// 源文件名
private String xxxContentType;// 源文件類型
---struts默認允許上傳的文件所占最大內存為2097152字節(不同計算機可
能略有不同)。如果要更改默認設置,可以參考如下“更改struts中的默
認設置”
更改struts中的默認設置(即修改如下配置文件):
ReferencedLibraries/struts2-core-2.1.8.jar/org-apache.struts2/default.properties
18、更改strts中的默認配置:
1.更改對客服端發送過來的參數進行解碼的方式:
<constant name="struts.i18n.encoding" value="gbk"/>(默認為utf-8)
2.更該允許客戶端上傳的最大文件大小值:
<constant name=”struts.multipart.maxSize” value=”100000000”/>
(默認大小為:2097152字節,約2M)
19、全局異常處理:
<!--全局的異常處理,當異常從Action中拋出時,會自動進入全局異常處理模塊-->
<global-results>
<!-- 錯誤處理 -->
<result name="error">/WEB-INF/jsp/error.html</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error">
</exception-mapping>
</global-exception-mappings>
20、用戶輸入不存在請求資源路徑(及action不存在)處理:
<!-- 指定一個默認響應的Action,當遇到例外的action請求,該項配置啟作用 -->
<default-action-ref name="defaultAction"/>
<!-- 采用重定向方式調用toLogin -->
<action name="defaultAction">
<result type="redirectAction">
<param name="namespace">/</param>
<param name="actionName">toLogin</param>
</result>
</action>
<!-- 顯示登錄頁面 -->
<action name="toLogin">
<result>/WEB-INF/jsp/login.jsp</result>
</action>
通過此配置后,用戶輸入非法請求資源路徑時便不會出現“404”錯誤提示了,而是友好地跳轉到登錄頁面。
======NETCTOSS項目========
1)項目描述(一期工程)
實現電信計費系統的賬號管理,業務管理,資費管理,管理員和角色管理的功能。
2)項目技術架構
主要采用Struts2,JDBC,Ajax,jQuery技術。
采用MVC結構分層思想開發。
表現層(V):JSP,Ajax,jQuery
控制層(C):Struts2控制器Filter+Action
業務層(M):JavaBean,Bean組件
數據訪問層(M):DAO+JDBC
3)項目工程結構
a.開發包引入
--Struts2開發包(5個核心+1個json響應包)
--Oracle驅動包
b.src源碼
org.tarena.netctoss.action
org.tarena.netctoss.action.fee
org.tarena.netctoss.action.account
org.tarena.netctoss.service
org.tarena.netctoss.dao
org.tarena.netctoss.pojo
org.tarena.netctoss.util
org.tarena.netctoss.test
c.配置文件
web.xml : 配置struts2控制器
struts.xml:struts2框架主配置
--struts-fee.xml : 資費模塊配置
--struts-account.xml:賬號模塊配置
d.WebRoot
WebRoot/WEB-INFO/jsp/fee/資費頁面
WebRoot/styles/樣式文件
WebRoot/js/腳本文件
WebRoot/images/頁面圖片
=========記錄檢索=========
search.action-->SearchAction-->DAO
1.項目評審
1)自我簡介,項目需求描述,功能演示
2)典型功能實現講解
2.共通問題處理
1)表單校驗處理(參考“jQuery表單校驗插件”目錄的資料)
可以利用jQuery編寫校驗函數,
也可以基于現有的jQuery擴展插件來實現
例如validate和formvalidate等插件都可以
2)Struts2全局異常處理
當異常拋出Action的業務方法時,可以利用全局異常處理方式解決(在struts.xml追加配置)。
3)將頁眉導航部分提取成一個head.jsp,
通過include方式引入各個頁面。
4)設置一個默認響應Action。
遇到不能識別的Action請求,或找不到請求對應的Action時,調用該默認Action做響應