1. 程式人生 > >Struts2的傳值方式及原理

Struts2的傳值方式及原理

evm method struts-2 hash xtend page getattr class blog

1.普通的傳值方式

UserActionForCommonParam類

Action類接收三個參數,分別是id,username,content.

  1. package com.struts.action;
  2. public class UserActionForCommonParam {
  3. private int id;
  4. private String username;
  5. private String content;
  6. public int getId() {
  7. return id;
  8. }
  9. public void setId(int id) {
  10. this.id = id;
  11. }
  12. public String getUsername() {
  13. return username;
  14. }
  15. public void setUsername(String username) {
  16. this.username = username;
  17. }
  18. public String getContent() {
  19. return content;
  20. }
  21. public void setContent(String content) {
  22. this.content = content;
  23. }
  24. // 從前臺頁面接收到參數後會在這個方法裏打印
  25. public String addUser() {
  26. System.out.println("ID: " + this.getId());
  27. System.out.println("Username: " + this.getUsername());
  28. System.out.println("Content: " + this.getContent());
  29. return "success";
  30. }
  31. }


對應的struts.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE struts PUBLIC
  3. "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
  4. "http://struts.apache.org/dtds/struts-2.3.dtd">
  5. <struts>
  6. <constant name="struts.enable.DynamicMethodInvocation" value="true"/>
  7. <constant name="struts.devMode" value="true"/>
  8. <package name="default" namespace="/" extends="struts-default">
  9. <action name="UserActionForCommonParam" class="com.struts.action.UserActionForCommonParam" method="addUser">
  10. <result name="success">/UserPage.jsp</result>
  11. </action>
  12. </package>
  13. </struts>


前臺頁面UserPage.jsp

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ taglib prefix="s" uri="/struts-tags" %>
  3. <html>
  4. <head>
  5. <title>UserPage</title>
  6. </head>
  7. <body>
  8. <%--下面的Table用於提交參數(id,username,content)--%>
  9. <form action="UserActionForCommonParam" method="POST">
  10. <table border="1">
  11. <tr>
  12. <td>ID:</td>
  13. <td><input type="text" name="id"/></td>
  14. </tr>
  15. <tr>
  16. <td>Username:</td>
  17. <td><input type="text" name="username"/></td>
  18. </tr>
  19. <tr>
  20. <td>Content:</td>
  21. <td><input type="text" name="content"/></td>
  22. </tr>
  23. <tr>
  24. <td colspan="2" align="center">
  25. <input type="submit" value="提交"/>
  26. </td>
  27. </tr>
  28. </table>
  29. </form>
  30. <br/>
  31. <%--下面的Table用於顯示傳遞回來id,username,content--%>
  32. <table border="1">
  33. <tr>
  34. <td>ID: </td>
  35. <td><input type="text" value="${id}"/></td>
  36. </tr>
  37. <tr>
  38. <td>Username: </td>
  39. <td><input type="text" value="${username}"/></td>
  40. </tr>
  41. <tr>
  42. <td>Content: </td>
  43. <td><input type="text" value="${content}"/></td>
  44. </tr>
  45. </table>
  46. <%--用於查看ValueStack中的傳遞的值--%>
  47. <s:debug/>
  48. </body>
  49. </html>

在實踐後會發現,如果遇到參數非常多的情況,那麽就需要在Action類中寫非常多的屬性以及對應的get/set方法.所以這種方式不太可取.解決問題的方法必然是封裝一個JavaBean.這就用到了Strut2的第二種傳值方式--DomainModel

2.DomainModel傳值

首先要創建一個存儲的JavaBean

User類

把id,username,content封裝的到一個User類當中.

  1. package com.struts.model;
  2. public class User {
  3. private int id;
  4. private String username;
  5. private String content;
  6. public int getId() {
  7. return id;
  8. }
  9. public void setId(int id) {
  10. this.id = id;
  11. }
  12. public String getUsername() {
  13. return username;
  14. }
  15. public void setUsername(String username) {
  16. this.username = username;
  17. }
  18. public String getContent() {
  19. return content;
  20. }
  21. public void setContent(String content) {
  22. this.content = content;
  23. }
  24. }

UserActionForDomainModel類

  1. package com.struts.action;
  2. import com.opensymphony.xwork2.ActionSupport;
  3. import com.struts.model.User;
  4. public class UserActionForDomainModel extends ActionSupport{
  5. private User user;
  6. public User getUser() {
  7. return user;
  8. }
  9. public void setUser(User user) {
  10. this.user = user;
  11. }
  12. public String addUser() {
  13. System.out.println("ID: " + user.getId());
  14. System.out.println("Username: " + user.getUsername());
  15. System.out.println("Content: " + user.getContent());
  16. return "success";
  17. }
  18. }


對應的struts.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE struts PUBLIC
  3. "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
  4. "http://struts.apache.org/dtds/struts-2.3.dtd">
  5. <struts>
  6. <constant name="struts.enable.DynamicMethodInvocation" value="true"/>
  7. <constant name="struts.devMode" value="true"/>
  8. <package name="default" namespace="/" extends="struts-default">
  9. <action name="userForDomainModel" class="com.struts.action.UserActionForDomainModel" method="addUser">
  10. <result name="success">/UserPage.jsp</result>
  11. </action>
  12. </package>
  13. </struts>


前臺頁面UserPage.jsp

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ taglib prefix="s" uri="/struts-tags" %>
  3. <html>
  4. <head>
  5. <title>UserPage</title>
  6. </head>
  7. <body>
  8. <%--要特別註意的是DomainModel的傳值方式必須要使用對象.參數方式進行賦值以及取值--%>
  9. <form action="userForDomainModel" method="POST">
  10. <table border="1">
  11. <tr>
  12. <td>ID:</td>
  13. <td><input type="text" name="user.id"/></td>
  14. </tr>
  15. <tr>
  16. <td>Username:</td>
  17. <td><input type="text" name="user.username"/></td>
  18. </tr>
  19. <tr>
  20. <td>Content:</td>
  21. <td><input type="text" name="user.content"/></td>
  22. </tr>
  23. <tr>
  24. <td colspan="2" align="center">
  25. <input type="submit" value="提交"/>
  26. </td>
  27. </tr>
  28. </table>
  29. </form>
  30. <br/>
  31. <%--下面的Table用於顯示傳遞回來id,username,content--%>
  32. <table border="1">
  33. <tr>
  34. <td>ID: </td>
  35. <td><input type="text" value="${user.id}"/></td>
  36. </tr>
  37. <tr>
  38. <td>Username: </td>
  39. <td><input type="text" value="${user.username}"/></td>
  40. </tr>
  41. <tr>
  42. <td>Content: </td>
  43. <td><input type="text" value="${user.content}"/></td>
  44. </tr>
  45. </table>
  46. <%--用於查看ValueStack中的傳遞的值--%>
  47. <s:debug/>
  48. </body>
  49. </html>

實際上User類不需要顯式的實例化,struts會自動幫你實例化,但前提條件是,傳值時需要使用對象.參數名的方式進行傳遞.

除了這種傳值方式外,struts2還提供另外一種傳值方式.

3.ModelDriven傳值

依然要創建User的JavaBean

User類

  1. package com.struts.model;
  2. public class User {
  3. private int id;
  4. private String username;
  5. private String content;
  6. public int getId() {
  7. return id;
  8. }
  9. public void setId(int id) {
  10. this.id = id;
  11. }
  12. public String getUsername() {
  13. return username;
  14. }
  15. public void setUsername(String username) {
  16. this.username = username;
  17. }
  18. public String getContent() {
  19. return content;
  20. }
  21. public void setContent(String content) {
  22. this.content = content;
  23. }
  24. }


UserActionForModelDriven類

  1. package com.struts.action;
  2. import com.opensymphony.xwork2.ModelDriven;
  3. import com.struts.model.User;
  4. public class UserActionForModelDriven implements ModelDriven<User>{
  5. private User user;
  6. public String addUser() {
  7. System.out.println("ID: " + user.getId());
  8. System.out.println("Username: " + user.getUsername());
  9. System.out.println("Content: " + user.getContent());
  10. return "success";
  11. }
  12. @Override
  13. public User getModel() {
  14. if (user == null) {
  15. user = new User();
  16. }
  17. return user;
  18. }
  19. }

這種方式可以不用在Action類中編寫對應的get/set方法,但是需要實例化User類.

前臺UserPage.jsp

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ taglib prefix="s" uri="/struts-tags" %>
  3. <html>
  4. <head>
  5. <title>UserPage</title>
  6. </head>
  7. <body>
  8. <form action="userForModelDriven" method="POST">
  9. <table border="1">
  10. <tr>
  11. <td>ID:</td>
  12. <td><input type="text" name="id"/></td>
  13. </tr>
  14. <tr>
  15. <td>Username:</td>
  16. <td><input type="text" name="username"/></td>
  17. </tr>
  18. <tr>
  19. <td>Content:</td>
  20. <td><input type="text" name="content"/></td>
  21. </tr>
  22. <tr>
  23. <td colspan="2" align="center">
  24. <input type="submit" value="提交"/>
  25. </td>
  26. </tr>
  27. </table>
  28. </form>
  29. <br/>
  30. <%--下面的Table用於顯示傳遞回來id,username,content--%>
  31. <table border="1">
  32. <tr><span id="transmark"></span>
  33. <td>ID: </td>
  34. <td><input type="text" value="${id}"/></td>
  35. </tr>
  36. <tr>
  37. <td>Username: </td>
  38. <td><input type="text" value="${username}"/></td>
  39. </tr>
  40. <tr>
  41. <td>Content: </td>
  42. <td><input type="text" value="${content}"/></td>
  43. </tr>
  44. </table>
  45. <%--用於查看ValueStack中的傳遞的值--%>
  46. <s:debug/>
  47. </body>
  48. </html>

頁面還是和普通傳值一樣.

可以去ValueStack當中觀察參數的存儲,更透徹的理解Struts2的參數傳遞. : <s:debug/>

1.什麽是ValueStack

對於每一個action的調用,Struts在執行相應的動作方法之前會先創建一個名為ValueStack的對象。Value Stack用來保存該動作對象或者對象。由於最終在執行Action類相應的action方法之前,攔截器需要先訪問ValueStack。視圖也需要訪問ValueStack。Struts框架將其保存在一個名為struts.valueStack的請求屬性中。

2.獲取valueStack的三種方法:

 ValueStack v1 = ActionContext.getContext().getValueStack();
     ValueStack v2 = ServletActionContext.getValueStack(ServletActionContext.getRequest());
        ValueStack v3 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");

打印其HashCode碼一模一樣。
說明:
1、 值是一樣的,說明只有一個對象
2、 因為有一種是從request域中獲取的,所以是一次請求

3.ValueStack的內容

valueStack分為對象棧(Object Stack)和Map棧(Context Map).

Struts將把動作和相關對象壓入Object Stack,而把各種各樣的映射關系壓入Context Map。

通常說的ValueStack就是指Object Stack,它是一個真正數據結構意義的棧。Object Stack也常常被稱為root(其實是一個ArrayList)。Context Map我們一般稱其為context(是一個HashMap)它是整個OGNL其中包括了root

4.OGNL中的兩種對象

OGNL中包含兩種對象,即_root和_value.
_value即:request, session, application, parameters, attr等命名對象,但這些命名對象都不是根。
_root即:當前請求的Action實例等
“根”對象和普通命令對象的區別在於:
“根”對象(root) 對象棧: 實際上是 CompoundRoot 類型, 是一個使用 ArrayList 定義的棧. 裏邊保存各種和當前 Action 實例相關的對象(如果這個Action實例包括域對象的話域對象會進root).是一個數據結構意義的棧.

1)訪問Context Map裏的對象需要在對象名之前添加 #
2)訪問Object Stack對象的屬性時,可以省略#而直接通過屬性名來搜索

技術分享

說明:
從上圖中也可以看出valueStack總共分為兩個部分:

      對象棧:root (CompoundRoot類)
      Map棧:_values和_root (OgnlContext 類)

5.Struts會把當前訪問的Action實例壓入值棧棧定

壓入時間點為:
由於Struts2最後終將調用Action類中的action方法。但在調用此方法之前:

*先創建一個StrutsActionProxy (ActionProxy默認實現的子類)
*在創建StrutsActionProxy之後,對其進行初始化,把Action對象壓入值棧

在調用該方法前該Action被壓入棧頂,如果在初始化的過程中,在Action的構造函數中又初始化了其他類,這這個類也會被壓入值棧。所以最後棧頂的元素會是這個新初始化的類。

6.ValueStack中對象的存取

Object Stack 的存放: push 或者 add:

將一個對象放入Object Stack的棧頂:

  • 1 ActionContext.getContext().getValueStack().push(object);
  • 2 ActionContext.getContext().getValueStack().getRoot().add(0,object);

Object Stack的提取: peek() 或 pop()

peek()取得的是棧頂元素:

  • 1 Object object = ActionContext.getContext().getValueStack().peek();

Object Stack的元素的彈出:pop:

  • 1 Object object = ActionContext.getContext().getValueStack().pop();

7.動態修改ValueStack中對象的屬性

技術分享

說明:
可以利用valueStack.setParameter方法改變對象棧中對象的屬性的值。至於匹配哪個。則依次從對象棧的棧頂向下搜索,找到匹配的就修改

Struts2的傳值方式及原理