【SSH網上商城專案實戰24】Struts2中如何處理多個Model請求
1. 問題的提出 |
Struts2中如果實現了ModelDriven<model>
介面就可以將傳來的引數注入到model中了,就可以在Action中使用該model,但是如果現在有兩個model都需要在同一個Action中使用該咋整呢?比如上一節我們完成了線上支付功能,但是支付完成了還沒結束,我們需要接收從第三方那邊反饋回來的資訊,比如成功支付後,我們需要給付款方傳送郵件和簡訊等。所以我們還需要在payAction中獲取從第三方傳過來的引數,由於從第三方傳過來的引數與我們傳過去的引數是不同的,所以接收那些引數我們也得寫一個Model(BackData),那麼問題來了,我們的PayAction已經寫成這樣子了:public class PayAction extends BaseAction<SendData>
ModelDriven<SendData>
介面了,那麼如何在一個action中再接收一個Model,並且還得對它們進行不同的處理呢?
有種解決辦法(其實也不能稱為解決辦法……因為根本就沒解決……)就是寫一個Model,然後讓SendData
和BackData
繼承它,但是問題是這兩個Model根本就沒關係,為啥要繼承同一個Model,所以這種解決辦法實際上是在逃避上面的問題。
在SpringMVC(SpringMVC還沒真正開始學,如果有說錯,請指正!)很好的解決了這個問題,因為SpringMVC中每個方法對應一個Model,而不是每個Action對應一個Model,這就方便了,我在同一個Action中寫兩個方法即可,不同的方法處理不同的Model。
2. 問題的解決 |
針對這個問題,Struts2也提供了一種解決辦法:
Struts2在ActionContext
中儲存了很多個Map,比如之前提到的request, session, application等,其中還有個parameterMap
,該Map中儲存了request所有的請求引數,只要我們的Action實現了parameterAware
介面,就能拿到這個parameterMap
,這就跟ModelDriven
的道理是一樣的,如果我們實現了ModelDriven<Model>
好了,那現在問題好辦了,支付的引數和返回的引數是不同的,也就是說兩次進入PayAcition
中的引數是不同的,即兩次的parameterMap
中裝的資料不一樣,那隻要我們在Action中選取一個引數(該引數只要能區分兩次是不同的request請求即可)作為判斷,就知道當前該用哪個Model來接收引數(SendData還是BackData)。下面我們改寫一下PayAction中的程式碼:
@Controller("payAction")
@Scope("prototype")
public class PayAction extends BaseAction<Object> implements ParameterAware { //注意上面繼承的BaseAction中不能寫SendData了,要寫Object,等下我們再判斷具體使用哪個 //定義一個Map接收request的請求引數 private Map<String, String[]> parameters; @Override public void setParameters(Map<String, String[]> parameters) { this.parameters = parameters; } /*在struts-default.xml文中,ServletConfig攔截器在ModelDriven之前先執行,所以我們在注入model的時候, request引數已經有了,這樣我們就可以在getModel()方法中通過引數來判斷是哪個請求了*/ @Override public Object getModel() { //付款的時候有支付通道編碼的引數(pd_FrpId),返回的時候沒有 //這樣我們就可以通過該引數判斷是支付還是返回了 if(parameters.get("pd_FrpId") != null) { model = new SendData(); } else { model = new BackData(); } return model; } //向易寶傳送資料的方法 public String goBank() { //對應傳送的model:SendData SendData sendData = (SendData)model; //處理髮送資料的邏輯,前一節已經實現過了…… } //接收返回的資料的方法 public void backBank() { //對應接收的model:BackData BackData backData = (BackData)model; //處理返回資料的邏輯……後面再來實現, //先講Struts2處理多個Model請求這個知識點 } }
3. Struts2的處理流程 |
我們再來分析一下Struts2的執行流程,這樣更加利於理解上面的原理。Struts處理流程:
- 獲取請求後,先建立Action的代理,在建立代理的時候順便建立了Action;
- 執行18個攔截器,攔截器執行成功後再呼叫Action的方法;
- Action的方法執行完畢後,再呼叫18個攔截器
所以根據這個流程,我們知道:先建立Action–>再執行攔截器(先執行ServletConfig
,再執行ModelDriven
,因為ServletConfig
攔截器配在ModelDriven
的前面)。所以在上面的程式碼中,我們才可以在getModel()
方法中去拿parameterMap
中的資料來進行判斷。
用下面簡單的時序圖來直觀的表示一下上面的處理流程吧:
這就很直觀的看出Struts2的處理流程了,那麼對於上面處理多個Model請求也很好理解了。到這裡,Struts2處理多個Model請求的方法部分已經分析完了,下面針對本專案中的一個小邏輯,做一下完善。
4. 完善接收資料的方法 |
上面遺留了一個邏輯的實現,即處理返回的資料,這裡的邏輯主要有:更新訂單狀態(已付款,已發貨等),傳送郵件,傳送簡訊等。我們先把更新訂單狀態完成,主語傳送郵件和傳送簡訊的功能,我們後面再寫。
先完善backBank()
方法:
public void backBank() {
BackData backData = (BackData)model;
System.out.println(model);
boolean isOK = payService.checkBackData(backData);
if(isOK) { //1. 更新訂單狀態,引數是自己根據資料庫中的情況傳進去的,用來測試 forderService.updateStatusById(Integer.valueOf(201605006), 2); //2. 根據user郵箱地址,傳送郵件 //3. 傳送手機簡訊 System.out.println("----success!!----"); } else { System.out.println("----false!!!----"); } }
然後我們完成payService中的checkBackData(backData)
方法(邏輯和21節中的基本一樣):
@Service("payService")
public class PayServiceImpl implements PayService { //省略不相關程式碼 /******************************上面是傳送請求的方法**************************************/ // 完成返回資料的追加 private String joinBackDataParam(BackData backData) { // 追加字串,為加密驗證做準備 StringBuffer infoBuffer = new StringBuffer(); infoBuffer.append(backData.getP1_MerId()); infoBuffer.append(backData.getR0_Cmd()); infoBuffer.append(backData.getR1_Code()); infoBuffer.append(backData.getR2_TrxId()); infoBuffer.append(backData.getR3_Amt()); infoBuffer.append(backData.getR4_Cur()); infoBuffer.append(backData.getR5_Pid()); infoBuffer.append(backData.getR6_Order()); infoBuffer.append(backData.getR7_Uid()); infoBuffer.append(backData.getR8_MP()); infoBuffer.append(backData.getR9_BType()); return infoBuffer.toString(); } // 對返回來的資料進行加密,並且和傳過來的密文進行比較,如果OK則說明資料沒有被篡改 public boolean checkBackData(BackData backData){ String joinParam=this.joinBackDataParam(backData); // 加密後得到自己的密文 String md5 = DigestUtil.hmacSign(joinParam.toString(),key); // 密文和傳過來密文比較 return md5.equals(backData.getHmac()); } }
最後我們完成ForderService中的updateStatusById
方法:
//ForderService介面
public interface ForderService extends BaseService<Forder> { //省略其他無關程式碼…… //根據訂單編號,更新訂單狀態 public void updateStatusById(int id, int sid); } //ForderServiceImpl實現類 @Service("forderService") public class ForderServiceImpl extends BaseServiceImpl<Forder> implements ForderService { //省略其他無關程式碼 @Override public void updateStatusById(int id, int sid) { String hql = "update Forder f set f.status.id=:sid where f.id=:id"; getSession().createQuery(hql) .setInteger("sid", sid) .setInteger("id", id) .executeUpdate(); } }
這樣就能在顧客付款後更新訂單狀態了。