Struts2 快速筆記2
路線:
- Struts2的概述、Struts2的入門、Struts2常見的配置、Struts2的Action的編寫
- Struts2的資料的封裝、結果頁面配置
- Struts2的值棧和OGNL表示式
- Struts2的標籤庫
上一篇:Struts2 快速筆記 1
1. OGNL表示式
-
OGNL:Object Graph Navigation Language,功能強大的表示式語言——比EL表示式,物件導航語言表示式。
- EL:只能從域物件中獲取資料,11個域物件——request/session/pagecontext等:${name},${request}
- OGNL:可以呼叫物件的方法,域物件資料,以及Struts2的值棧的資料。OGNL是獨立的第三方語言,Struts將其引用到了,單獨也可以使用。
-
OGNL的功能:
- 支援運算子操作
- struts2中支援物件方法的呼叫
- Struts2中支援靜態方法和值的引用
- 支援賦值和表示式串聯
- 訪問OGNL上下文OGNLContext和ActionContext——與值棧有關
- 操作集合物件,或者new新的物件
-
使用3要素:Java中使用,不僅僅在struts2
- 表示式:從哪裡獲取什麼資料
- 根物件:操作目標
- Context物件:在哪裡操作,操作環境
示例(Java中):
-
包:
-
Demo
@Test /** * OGNL呼叫物件方法 */ public void demo1() throws OgnlException{ //3要素 context root source OgnlContext ognlContext = new OgnlContext(); Object root = ognlContext.getRoot(); Object obj = Ognl.getValue("'helloworld'.length()", ognlContext, root); System.out.println(obj); } @Test /** * OGNL呼叫靜態方法和值: * @物件全路徑名@方法(引數) * @物件全路徑名@變數名 */ public void demo2() throws OgnlException{ //3要素 context root source OgnlContext ognlContext = new OgnlContext(); Object root = ognlContext.getRoot(); Object obj = Ognl.getValue("@
-
OGNL在Struts2中使用:主要是從JSP頁面的資料互動
-
頁面引入標籤:
<%@ taglib uri="/struts-tags" prefix="s" %>
-
獲取物件資料
<h1>Struts2的OGNL表示式</h1> <h3>1.訪問物件方法</h3> <s:property value="'hello'.length()"/> <br/> <h3>2.訪問靜態方法-----struts2 預設將其關閉,如@[email protected]()-------可以在struts的核心包中default.properties中配置常量struts.ognl.allowStaticMethodAccess=true</h3> <s:property value="@java.lang.[email protected]()"/> <br/> <h3>3.訪問靜態成員-----struts2 可以如@[email protected]</h3> <s:property value="@[email protected]"/>
-
OGNL其最主要的作用——值棧
-
2. 值棧空間
-
概念
ValueStack,是一個容器(棧),有struts框架建立。當前端頁面如JSP傳送一個請求時,Struts的預設攔截器就會將請求中的資料進行封裝,然後將其放入ValueStack的棧頂。
前端的請求將會被封裝成物件,放入值棧容器中。在使用Struts2框架的開發中,一般前端資料的中轉都由值棧來進行,儘量較少的使用域物件(request,pageContext等),但也要記住struts中也能使用這些物件。有一個Action的例項,就會有一個值棧的物件,Action是多例的。
**作用:**存入ValueStack中的資料,在struts2中任何位置能獲取到,如頁面、配置檔案、Action中;而存入域物件如Request物件的資料,只能在JSP頁面中獲取。
-
內部結構
值棧中的兩個主要區域:root和context。root區繼承了一個ArrayList,context區來自OGNL的包,其實現了一個Map介面,包含了一些常見的Web物件的引用。
-
root區一般放置常見的物件,ArrayList——CompoundRoot物件;獲取root資料不需要加#
-
context放置物件的引用,Map——OgnlContext物件;獲取context資料需要加#
- root引用
- request
- parameters
- session
- application
- attr:小範圍的引用,使用它按序依次訪問request、session、application中的屬性,找到即停。
-
一般而言,操作的值棧是指的操作Root區。
-
也可以在JSP的debug中看到
<h1>OGNL的值棧內部結構debug</h1><s:debug />
-
-
值棧與ActionContext
和ServletContext上下文物件一樣,ActionContext對應Action的生命週期。因Struts核心過濾器
StrutsPrepareAndExecuteFilter
在init
方法中會執行載入配置檔案(xml中繼承自struts-default的,又會執行許多預設的攔截器,實現引數的校驗,型別轉化和封裝等功能)。ActionContext:請求產生------
StrutsPrepareAndExecuteFilter
------執行doFilter方法-----建立ActionContext-----再由其建立ValueStack,並封裝到ActionContext中;所有可以使用ActionContext獲取值棧物件。ActionContext有值棧的引用,於是能夠訪問Servlet中的request等域物件,就獲得其API的相關資料。ActionContext中的獲得的上下文物件,其實是通過當前執行緒來獲得的:
public class ActionContext implements Serializable { static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>(); /** * Constant for the name of the action being executed. */ public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name"; /** * Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}. */ public static final String VALUE_STACK = ValueStack.VALUE_STACK; /** * Constant for the action's session. */ public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session"; ...... /** * Returns the ActionContext specific to the current thread. * * @return the ActionContext for the current thread, is never <tt>null</tt>. */ public static ActionContext getContext() { return actionContext.get(); }
-
獲得值棧
- 通過ActionContext獲取
- 在Struts2中也在request中存入了一個值棧引用
//方式1. ValueStack valueStack1 = ActionContext.getContext().getValueStack(); // 方式2. ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest() .getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); // 二者屬於同一Action,ValueStack當然是同一個物件 System.out.println(valueStack1 == valueStack2); return NONE;
-
操作值棧:操作資料
-
通過Action提供了Get方法的屬性,獲取其屬性值。
- 原因:預設情況下,ValueStack會將Action物件本身也壓入棧(Root區)中,所有有get方法的屬性都可獲取
public class DoValueStack extends ActionSupport { private User user; //方式1必須要有get屬性 public User getUser() { return user; } @Override public String execute() throws Exception { //方式1.在action中提供get方法 user = new User("Helo", "1234"); return SUCCESS; } } -----------------------JSP---------------------------------- <h2>操作值棧方式一,get</h1> <s:property value="user.username"/> <s:property value="user.password"/>
-
通過ValueStack自身的API:更常用,不需要太多的Get方法,程式碼更可讀
@Override public String execute() throws Exception { //方式2.使用ValueStack本身的API ValueStack valueStack = ActionContext.getContext().getValueStack(); //新增資料,push(物件),set(key,value) User user = new User("ooooo", "4444"); valueStack.push(user); //push的物件儲存在棧頂位置!!!!!!!!!!!!!!!! valueStack.set("aaa", "123456"); return SUCCESS; } ----------------------------JSP--------------------------------- <s:property value="username"/> ooooo <s:property value="aaa"/> 123456
-
-
獲取值棧資料
-
獲取Root區中的資料——獲取一個物件或者集合
public String execute() throws Exception { // ValueStack valueStack = ActionContext.getContext().getValueStack(); //1個物件 User user = new User("張三", "123456"); valueStack.push(user); List<User> list = new ArrayList<>(); list.add(new User("李四", "123")); list.add(new User("王五", "123")); list.add(new User("趙六", "123")); valueStack.set("list", list); return SUCCESS; } ----------------------------JSP-------------------------- <h2>1.獲取Root區的資料 單個物件</h1> <s:property value="username"/> <s:property value="password"/> <h2>2.獲取Root區的資料 集合</h1> <s:property value="list[0].username"/> <s:property value="list[0].password"/><br/> <s:property value="list[1].username"/> <s:property value="list[1].password"/><br /> <s:property value="list[2].username"/> <s:property value="list[2].password"/>
-
獲取context區中的資料(不常用)
ServletActionContext.getRequest().setAttribute("myname", "孫七"); ServletActionContext.getRequest().getSession().setAttribute("myname", "朱八"); ServletActionContext.getServletContext().setAttribute("myname", "周九"); ------------------------------JSP--------------------------- <h2>3.獲取context區的資料 </h1> <s:property value="#request.myname"/> <s:property value="#session.myname"/> <s:property value="#application.myname"/> <s:property value="#attr.myname"/> <h2>4.獲取請求字串引數 </h1> <s:property value="#parameters.id"/>
-
-
EL為何能訪問值棧資料
原始碼:Struts中初始化時對request進行了增強包裝(request.getAttribute),框架將其進行了改造。使用EL表示式時,先會訪問原request等域物件中資料,沒有獲取到時則進入ActionContext中,進入值棧進行查詢。
於是,EL表示式同樣能獲取到值棧資料。
3. OGNL 中的特殊字元
-
<% request.setAttribute("name", "張三"); %> <h2>OGNL中#號的使用</h2> <h3>1.獲取context域中的資料</h3> <s:property value="#request.name"/> <h3>2.構造集合list</h3> <s:iterator var="i" value="{'aa','bb','bb'}"> <s:property value="i"/>------<s:property value="#i"/><br/> </s:iterator> <h3>2.構造集合map1</h3> <s:iterator value="#{'aa':'11','bb':'22','cc':'33'}"> <s:property value="key"/>------<s:property value="value"/><br/> </s:iterator> <h3>2.構造集合map2, 使用了var就必須加#號,從context區中獲取</h3> <s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33'}"> <s:property value="#entry.key"/>------<s:property value="#entry.value"/><br/> </s:iterator> <h3>3.#號在一些常用表單中使用,構建集合</h3> 性別:<input type="radio" value="1" name="sex1">男 <input type="radio" value="2" name="sex1">女 <br> <h4>3.1使用list</h4> <s:radio list="{'男','女'}" name="sex2" label="性別"></s:radio> <h4>3.2使用map</h4> <s:radio list="#{'1':'男','2':'女'}" name="sex3" label="性別"></s:radio>
-
%
常用於在HTML標籤中,顯示獲取資料(也可以直接巢狀,但在OGNL的標籤中無法巢狀);也就是強制解析OGNL語句,同樣也可以強制失效OGNL
<s:textField name="name" value="%{#request.name}"/> <input type="text" name="name2" value="<s:propery value='#request.name'>"> //可巢狀 <s:property value="%{‘#request.name’}"/>
-
$
一般在配置檔案xml或properties中使用,來使用OGNL獲得資料。
- 屬性檔案:使用$來限定OGNL表示式,否則會被視為字串,如${#session.user.username}
- xml:同上使用
案例:查詢優化,將查詢的資料存入值棧中,在頁面使用OGNL獲取。
4. Struts攔截器
-
interceptor,作用時攔截(過濾)Action;相比Filter過濾的是客戶端向伺服器傳送的請求,而攔截器攔截的是客戶端對Action的訪問,粒度更細,可以攔截到Action的具體方法。攔截器可以用來做一些許可權,控制之類的功能。
Struts2框架核心的功能都是依賴攔截器實現
-
Struts的執行流程
客戶端向伺服器傳送一個Action的請求,執行核心過濾器(doFilter)方法。在這個方法中,呼叫executeAction()方法,在這個方法內部呼叫dispatcher.serviceAction();在這個方法內部建立一個Action代理,最終執行的是Action代理中的execute(),在代理中執行的execute方法中呼叫ActionInvocation的invoke方法。在這個方法內部遞迴執行一組攔截器(完成部分功能),如果沒有下一個攔截器,就會執行目標Action,根據Action的返回的結果進行頁面跳轉。
-
自定義攔截器:編寫一個類實現Interceptor介面或者繼承AbstractInterceptor類;同樣,在設定默寫許可權的時候,可以繼承MethodFilterInterceptor,來完成更小粒度(基於方法)的控制和過濾。
public class InterceptorDemo3 extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("intercepto3 EXE。。。。"); //遞迴執行其他的攔截器 String obj = invocation.invoke(); System.out.println("intercepto3 Done。。。。"); return obj; } }
配置攔截器:
<package name="demo1" extends="struts-default" namespace="/"> <!-- 配置攔截器 --> <interceptors> <interceptor name="interceptorDemo1" class="com.leehao.strutslearning.interceptor.InterceptorDemo1" /> <interceptor name="interceptorDemo2" class="com.leehao.strutslearning.interceptor.InterceptorDemo2" /> <interceptor name="interceptorDemo3" class="com.leehao.strutslearning.interceptor.InterceptorDemo3" /> </interceptors> <action name="actionDemo1" class="com.leehao.strutslearning.action.ActionDemo1"> <result>/demo1.jsp</result> <!-- 將攔截器引入棧,注:一旦添加了自定義的攔截器,則必須要手動選擇是否執行預設的那一堆攔截器 --> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="interceptorDemo1"></interceptor-ref> <interceptor-ref name="interceptorDemo2"></interceptor-ref> <interceptor-ref name="interceptorDemo3"></interceptor-ref> </action> </package>
方法二:
<package name="demo1" extends="struts-default" namespace="/"> <!-- 配置攔截器 --> <interceptors> <interceptor name="interceptorDemo1" class="com.leehao.strutslearning.interceptor.InterceptorDemo1" /> <interceptor name="interceptorDemo2" class="com.leehao.strutslearning.interceptor.InterceptorDemo2" /> <interceptor name="interceptorDemo3" class="com.leehao.strutslearning.interceptor.InterceptorDemo3" /> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="interceptorDemo1"></interceptor-ref> <interceptor-ref name="interceptorDemo2"></interceptor-ref> <interceptor-ref name="interceptorDemo3"></interceptor-ref> </interceptor-stack> </interceptors> <action name="actionDemo1" class="com.leehao.strutslearning.action.ActionDemo1"> <result>/demo1.jsp</result> <interceptor-ref name="myStack"></interceptor-ref> </action> </package>
-
-
注意點
- 使用攔截器是在各個action中配置的,建議使用method粒度的攔截器;
- 攔截器主要針對不同的action,頁面如JSP的訪問使用過濾器來進行控制
- 攔截器可以分別在action中配置,也可以設定為攔截器的棧,按名稱引用;
- 一定記得同時配置預設的攔截器棧——defaultStack
5. Struts 的標籤
struts有自己的標籤,主要的好處是方便資料的記錄和回顯。分為通用標籤(語句)和UI標籤(表單等)。
-
通用標籤——表示式
最常用:
- 判斷標籤if
- 迭代標籤iterator
- property
- debug
- date
-
UI標籤——常用作資料回顯,且有自己的樣式,較為靈活。可以在struts.properties中設定屬性,也可以對單個的標籤進行theme=simple的設定,取消預設為xhtml的樣式。