一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 詳解Java的Struts2框架的結構及其數據轉移方式

詳解Java的Struts2框架的結構及其數據轉移方式

2020-03-21 13:33cdai JAVA教程

這篇文章主要介紹了詳解Java的Struts2框架的結構及其數據轉移方式,Struts框架是Java的SSH三大web開發框架之一,需要的朋友可以參考下

Struts2的結構

1.為什么要使用框架?

(1)框架自動完成了很多瑣屑的任務

對于Struts2來說,它幫助我們方便地完成了數據類型轉換、數據驗證、國際化等等
Web開發中常見的任務。還有Spring中大量使用的Template模式,都是在讓我們的開發
過程更加自動化、智能化。使用框架就是避免重新發明輪子,重新復制這些模板代碼。
框架讓我們將精力更多地放在更高級別的問題上,而不是常見工作流和基礎任務上。

(2)使用框架就是優雅地繼承了框架背后的架構

框架背后的架構通常定義了一系列的工作流程,我們要做的就是將特定應用的代碼
依附到這套流程上,這樣就可以享受到框架帶來的種種好處了。有些時候我們也可以
反抗框架的架構規則,但框架通常以一種很難被拒絕的方式提供它的架構。如此簡單
就可以優雅地繼承一個優秀的架構,而且是免費的,何樂而不為呢?

(3)使用框架更容易找到訓練有素的人

我之前所在公司整個項目幾乎都沒有用過什么框架,從Service服務的查找(類似JNDI)
到日志打?。愃芁og4j),再到數據庫連接池(類似DBCP),全都是內部人員自己
實現的。一來是因為項目比較老,當時可能還沒有什么開源框架可供使用,二來也是因為
公司保守的策略,擔心使用不穩定的開源框架可能會給項目帶來風險。這在當時的環境下
也許是沒錯的,公司高層自然會從更大的視角來考慮整個項目。

但是當項目逐漸龐大起來,同時世界上優秀的開源框架越來越多時,如果不能及時重構
并引入一些成熟的開源框架,最后的結果可能就是新招來的開發人員必須從頭開始學習
這個復雜的系統(都是內部系統,網上也沒有文檔幫助),還要小心內部框架的種種Bug,
成本真是太高了。

(4)內部框架跟不上行業的發展

前面說到了內部框架的Bug。對于開源框架,可能會有框架創始者團隊、大批的開源愛好者、
開源社區來支持。人民的力量是無窮的,Bug的修復速度可想而知,這點從最近開源后的
TextMate的Bug修復進程就可以看出了。很多擱置了很久的Bug在開源后被愛好者們迅速
解決,而內部框架呢?在當初開發他的人員離開公司后,在沒有重大Bug時甚至都不會有人
去讀他的源代碼吧,差距可見一斑!

(5)當然使用框架也不是一本萬利的事情

前面也提到過,使用不成熟的框架是有風險的,對于一個不是那么激進的項目還是保守為好。
(除非這是一群自由沒拘束的技術狂熱分子,可以自行決定使用什么框架,那真是幸福的事)
就像我以前用過的Java的HA高可用性服務Sequioa一樣,這個框架最終不再被開發公司提供支持
了,這時風險就更大了。

此外,使用一些不常見的框架時還要注意框架源碼的License協議,不要在項目中隨意引用、
修改框架的源碼以免引起不必要的法律糾紛。


2.Struts2背后的架構

既然前面已經分析了框架的這么多好處,那我們自然會開始學習使用Struts2了。但使用Struts2
會繼承什么樣的優雅架構呢?其實從較高的抽象層次上看,它依然是我們熟悉的MVC模式。

詳解Java的Struts2框架的結構及其數據轉移方式

對應之前HelloWorld的例子來看,控制器C(FilterDispatcher)也就是我們在web.xml中聲明的
Struts2核心類。而模型M就是我們的NewsAction動作類。而視圖V自然就是news.jsp了。模型
的概念似乎有些模糊,什么是模型呢?其實這個聽起來很名詞的概念在Struts2中既包含了靜態
從Web前端傳來的業務數據,也包含了業務邏輯的實現。

有人可能會說這種架構沒什么新意嘛,MVC框架有很多,這跟其他框架有什么區別呢?讓我們
站在低一級別的抽象層次上解剖Struts2,看看它有什么與眾不同。

詳解Java的Struts2框架的結構及其數據轉移方式

乍看十分復雜,如果只從用戶角度來看,在開發時我們只需要實現黃色的部分,也就是我們
HelloWorld實例中的struts.xml,NewsAction和news.jsp。這就是我們要做的全部,就如前面
說的,只需要做很少的事情,我們就成為了這個優秀架構的一部分。

現在來看其他部分。FilterDispatcher就是我們配置在web.xml中的Servlet過濾器,這是Struts2
的入口,所有Struts2的Web應用都要這樣配置。接下來藍色和綠色的部分就是Struts2的核心
了,可以說這些類都是Struts2的開發人員精心設計架構的。

(1)客戶端發送請求,J2EE容器解析HTTP包,將其封裝成HttpServletRequest。

(2)FilterDispatcher攔截到這個請求,并根據請求路徑到ActionMapper中查詢決定調用哪個Action。

(3)根據ActionMapper的返回結果,FilterDispatcher委托ActionProxy去struts.xml中找到這個Action。

(4)ActionProxy創建一個ActionInvocation,開始對Interceptor和Action進行遞歸調用。

(5)各個Interceptor完成各自任務

(6)真正對Action的調用,返回結果路徑

(7)Result對象將返回數據輸出到流中

(8)返回HttpServletResponse給J2EE容器,容器發送HTTP包到客戶端。

這就是Struts2的執行流程,核心對象是ActionInvocation和Interceptor,以及還未介紹的ActionContext。
ActionInvocation是整個流程的總調度,它跟Spring AOP中的Invocation對象很像。而Interceptor有很多
都是Struts2自帶的,最重要的是保存請求參數,并將前臺的數據傳遞到Action的成員變量上。

而ActionContext就是保存這些數據的全局上下文對象,最重要的是用來保存Action實例的ValueStack。
所謂全局是指ActionContext可以在Action以及Result中訪問,其實它是ThreadLocal類型。每個請求線程
都會有自己的Action和ActionContext實例。

可以說學習Struts2主要就是學習:

(1)讓Interceptor和Action配合完成任務。

(2)將前臺數據保存到Action中。

(3)Result通過ValueStack從Action中得到返回數據。


3.Struts2與Struts1的不同點

從上面的執行流程已經可以看出Struts1和2的巨大區別。

(1)ActionForm哪去了?Action還是那個Action嗎?

最明顯的就是我們在整個流程中都看不到ActionForm對象了,而且Action雖然還是叫這個名字,但是
看起來已經跟Struts1中的Action完全不同了。

首先ActionForm被拋棄了,從前臺傳來的數據已經可以保存到任意POJO了。先存到ActionForm再復制
到Dto對象的日子已經是過去了。第二,這個POJO其實是Action對象中的一個成員變量。這在Struts1
中所有請求共享一個Action實例時是不可能的,現在Struts2會為每個請求都創建一個Action實例,所以
這樣做是行得通的。第三,雖然這樣可行,可是看起來好像Action作為MVC中的模型M既保存數據,又
包含了業務邏輯,這是不是不良的設計?。科鋵嵶屑毾胂?,這樣的設計很方便,我們已經得到了數據,
直接就可以去操作Service層了。Action的職責看似多了,其實并不多。

(2)前端Servlet怎么變成了Filter?

我們知道Struts1和Spring MVC都是通過前端Servlet來作為入口的,為什么Struts2要用Servlet的過濾器呢?
因為Struts2是基于Webwork核心的,與Struts1已經完全不同了。Webwork可以說降低了應用程序與J2EE
API的耦合,比如將ActionServlet改為Servlet的Filter,再比如對HttpServletRequest/Response的直接訪問,
又如任何POJO都能擔任ActionForm的角色,任何類不用實現Action接口就可以作為Action使用等等,
因此Struts2也繼承了這種優秀的非侵入式設計。

這點與Spring的設計思想有些相像。比如那些Ware接口,不關心的Bean完全不需要實現,盡量降低應用
程序代碼與框架的耦合。侵入性的確是框架設計時要考慮的一個重要因素。

(3)Filter、Action、Result間的粘合劑OGNL

下圖可以清晰明了地展示出OGNL是如何融入Struts2框架的。

詳解Java的Struts2框架的結構及其數據轉移方式

在輸入頁面InputForm.html和返回頁面ResultPage.jsp使用Struts2標簽中訪問Action中的數據是如此方便,
OGNL使訪問ValueStack中保存的Action的屬性就像訪問ValueStack自己的屬性一樣方便。

對OGNL的大量使用是Struts2的一大特色。包括前臺標簽傳值到Action,Result從Action中取值等都會大量
用到OGNL。而OGNL中大量用到了反射,我想也許這是Struts2性能不如Struts1的一個原因吧。畢竟獲得了
靈活而低耦合的架構的同時是要付出一定代價的。

(4)Interceptor的強是無敵的強

Struts2中另一個強大的特性就是Interceptor攔截器了。Struts2內建了大量的攔截器,攔截器使大量代碼可以
重復使用,自動化了之前我們所說的瑣屑的任務,從而使Struts2達到了高水平的關注分離。這真是AOP思想
在框架中應用的典范!


Struts2三種數據轉移方式
Struts2提供了JavaBean屬性,JavaBean對象,ModelDriven對象三種方式來保存HTTP請求中的參數。下面通過一個最常見的
登錄的例子來看下這三種數據轉移方式。頁面代碼很簡單,提交表單中包含有用戶名和密碼,在Action中得到這兩個參數從而
驗證用戶是否登錄成功。

一、JavaBean屬性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%@ page contentType="text/html;charset=UTF-8" %>
 
<html>
 
<head></head>
 
<body>
  <h1>登錄頁</h1>
   
  <form action="/cdai/login" method="post">
   
    <div>
      <label for="username">名稱:</label>
      <input id="username" name="username" type="textfield"/>
    </div>
     
    <div>
      <label for="password">密碼:</label>
      <input id="password" name="password" type="password"/>
    </div>
     
    <div>
      <label for="rememberMe">
        <input id="rememberMe" name="rememberMe" type="checkbox"/> 記住我
      </label>
      <input type="submit" value="登錄"></input>
    </div>
     
  </form>
   
</body>
 
</html>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.cdai.web.ssh.action;
 
import com.cdai.web.ssh.request.LoginRequest;
import com.cdai.web.ssh.service.UserService;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ModelDriven;
 
public class LoginAction implements Action {
 
  private String username;
   
  private String password;
   
  private UserService userService;
   
  @Override
  public String execute() {
     
    System.out.println("Login action - " + request);
     
    return SUCCESS;
  }
 
  public String getUsername() {
    return request;
  }
 
  public void setUsername(String username) {
    this.username = username;
  }
   
  public String getPassword() {
    return request;
  }
 
  public void setPassword(String Password) {
    this.Password = Password;
  }
   
}

這種方式比較簡明,直接將表單中的參數保存到Action中的屬性中。Action在驗證時可能還需要將用戶名和密碼再封裝成Dto的
形式傳給Service層進行驗證。所以為什么不更進一步,直接將用戶名和密碼保存到Dto中。


二、JavaBean對象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%@ page contentType="text/html;charset=UTF-8" %>
 
<html>
 
<head></head>
 
<body>
  <h1>登錄頁</h1>
   
  <form action="/cdai/login" method="post">
   
    <div>
      <label for="username">名稱:</label>
      <input id="username" name="request.username" type="textfield"/>
    </div>
     
    <div>
      <label for="password">密碼:</label>
      <input id="password" name="request.password" type="password"/>
    </div>
     
    <div>
      <label for="rememberMe">
        <input id="rememberMe" name="rememberMe" type="checkbox"/> 記住我
      </label>
      <input type="submit" value="登錄"></input>
    </div>
     
  </form>
   
</body>
 
</html>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.cdai.web.ssh.action;
 
import com.cdai.web.ssh.request.LoginRequest;
import com.cdai.web.ssh.service.UserService;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ModelDriven;
 
public class LoginAction implements Action {
 
  private LoginRequest request;
   
  private UserService userService;
   
  @Override
  public String execute() {
     
    System.out.println("Login action - " + request);
     
    return SUCCESS;
  }
 
  public LoginRequest getRequest() {
    return request;
  }
 
  public void setRequest(LoginRequest request) {
    this.request = request;
  }
   
}

這樣就可以很方便地直接調用Service層了。但是有一個小缺點就是這樣加深了頁面參數名的深度,只有為參數名加上request
前綴(Action中的屬性名)才能使Struts2通過OGNL將表單中的參數正確保存到request對象中。


三、ModelDriven對象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%@ page contentType="text/html;charset=UTF-8" %>
 
<html>
 
<head></head>
 
<body>
  <h1>登錄頁</h1>
   
  <form action="/cdai/login" method="post">
   
    <div>
      <label for="username">名稱:</label>
      <input id="username" name="username" type="textfield"/>
    </div>
     
    <div>
      <label for="password">密碼:</label>
      <input id="password" name="password" type="password"/>
    </div>
     
    <div>
      <label for="rememberMe">
        <input id="rememberMe" name="rememberMe" type="checkbox"/> 記住我
      </label>
      <input type="submit" value="登錄"></input>
    </div>
     
  </form>
   
</body>
 
</html>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.cdai.web.ssh.action;
 
import com.cdai.web.ssh.request.LoginRequest;
import com.cdai.web.ssh.service.UserService;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ModelDriven;
 
public class LoginAction implements Action, ModelDriven<LoginRequest> 
{
 
  private LoginRequest request = new LoginRequest();
   
  private UserService userService;
   
  @Override
  public String execute() {
     
    System.out.println("Login action - " + request);
     
    return SUCCESS;
  }
 
  @Override
  public LoginRequest getModel() {
    return request;
  }
   
}

這種方式要多實現一個ModelDriven接口,將ModelDriven提供的對象也保存到ValueStack上,從而使前臺頁面可以直接通過
username和password屬性名來定義表單的參數名了。

三種方式具體采用哪種不能一概而論,還是看項目的具體需求再自己定吧!

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成人国产午夜在线视频 | 美国videos | 吻戏辣妞范1000免费体验 | 三上悠亚精品专区久久 | 国产专区亚洲欧美另类在线 | 亚州精品永久观看视频 | 色天天久久 | 91久久精品青青草原伊人 | 我与恶魔的h生活ova | 亚洲 日韩 在线 国产 视频 | 波多野结衣一区 | 日韩精品1| 成年看片免费高清观看 | 日本乱人伦中文在线播放 | 国产欧美日韩专区毛茸茸 | 亚洲精品123区在线观看 | 国产亚洲自愉自愉 | 狠狠撸在线影院 | 996免费视频国产在线播放 | 成人免费视屏 | 5g影院成人| 精品国产福利一区二区在线 | 人人干97 | 香蕉久久ac一区二区三区 | 免费观看视频在线 | 白丝校花被扒开双腿喷水小说 | 500福利第一导航 | 无人影院在线播放 | 精品久久久久久久久久久久久久久 | 日韩激情视频在线观看 | 日本片免费观看一区二区 | 91中文在线 | 亚洲国产精品嫩草影院永久 | a韩剧 | 国产a一级毛片午夜剧院 | 国产成人一区二区三区视频免费蜜 | 欧美日韩中文国产一区二区三区 | 香蕉国产人午夜视频在线 | 成人午夜视频一区二区国语 | 日本xxxⅹ69xxxx护士 | 男女拍拍拍免费视频网站 |