一、Struts與OGNL的結合原理app
(1)值棧:jsp
OGNL表達式要想運行就要準備一個OGNLContext對象,Struts2內部含有一個OGNLContext對象,名字叫作值棧。ide
值棧也由兩部分組成,一部分叫作root,裏面放置的是棧,另外一部分是context放入的是數據中心。函數
(2)棧:源碼分析
利用了棧先進先出的特色,每次放入元素的時候是放入到索引爲零的位置,取出的時候也是取出索引爲零的元素,即:從下圖的最上方元素取,符合棧的先進先出的特性。this
二、<s:debug>標籤(查看值棧中的內容)spa
(1)root(棧)debug
默認狀況下棧中存放當前訪問的Action。3d
(2)context:code
存放如下這些東西:
即:
三、Struts2與OGNL結合的體現
(1)Action以屬性名的方式獲取數據解析:
直接輸出屬性名,保持和表單的name屬性的值相同。
表單將數據提交給攔截器,攔截器獲取到「name=zhai」後,交給OGNL處理,OGNL從root中拿到name屬性並賦值爲「zhai」,由此,Action獲取到了表單提交的數據。
(2)對象方式:
表單:
獲取:
OGNL從棧頂得到user對象後,得到其name屬性並將其值設置爲「zhai」,由此,Action獲取到了表單提交的數據。
(3)模型驅動
OGNL獲取到name=zhai後,在值棧的root中,將user壓入棧頂,並給user中的name賦值,也就是說從棧頂取元素並賦值。
原理演示:
建立一個Action:
public class ActionStack extends ActionSupport implements Preparable { User user=new User(); public String execute(){ System.out.println(user); return "success"; } @Override public void prepare() throws Exception { ValueStack valueStack=ActionContext.getContext().getValueStack(); valueStack.push(user); } }
實現Preparable接口的緣由:
要在賦值前將user壓入棧頂 ,而將獲取棧的代碼和將user對象壓入棧頂的代碼寫入到prepare()函數中的目的正是獲取棧並在賦值前將user對象壓入棧頂。
源碼分析:
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="datetime"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> <interceptor-ref name="deprecation"/> </interceptor-stack>
在衆多的攔截器中,params攔截器位置相對靠後,所以須要將params攔截器放到prepare攔截器的位置,以實現賦值前將user壓入棧頂。
那麼如何操做呢?打開prepare攔截器的源碼可知要調用prepare攔截器須要實現Preparable接口並調用 prepare()方法。
配置Struts的配置文件、建立表單、User類後將表單數據提交給Action:
<body>
<form action="${pageContext.request.contextPath}/s/ActionStack">
用戶名:<input type="text" name="username"/><br>
<input type="submit" value="提交"/>
</form>
</body>
public class ActionStack extends ActionSupport implements Preparable { User user=new User(); public String execute(){ System.out.println(user); return "success"; } @Override public void prepare() throws Exception { ValueStack valueStack=ActionContext.getContext().getValueStack(); valueStack.push(user); } }
四、在struts.xml配置文件中的使用
(1)建立兩個Action:
Action1:
public class Action1 extends ActionSupport { private String name; public String execute(){ name="zhang"; return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Action2:
public class Action2 extends ActionSupport { public String execute(){ return "success"; } }
(2)配置Struts.xml配置文件,使得訪問Action1的時候可以重定向到Action2,:
<struts> <package name="action" namespace="/a" extends="struts-default"> <action name="Action2" class="pers.zhb.hello.Action2" method="execute"> <result name="success" type="dispatcher">hello.jsp</result> </action> <action name="Action1" class="pers.zhb.hello.Action1" method="execute"> <result name="success" type="redirectAction"> <param name="actionName">Action2</param> <param name="namespace">/a</param> <param name="name">${name}</param> </result> </action> </package> </struts>
(3)訪問Action1:
也就是說在配置struts.xml配置文件的時候,在<param>標籤內部將name屬性的值設置爲屬性不能識別的值,就能夠在重定向的時候將參數加到另一個Action。