在Java後臺編程中,大家一般會使用MVC設計模式,即便使用的具體框架不盡相同。今天,我們來說說MVC中的這個C,也就是Controller。Controller是web程序中最先接觸到用戶request的地方,當然,前提是該request經過了身份認證和權限檢查等重重考驗,這一部分建議在框架的Interceptor中進行。詳細內容請看筆者之前的博客玩轉Spring!從拒絕Filter開始。好了,話不多說,進入正題。
一般來說,Controller的作用是爲每個URI提供一個處理方法,比如有一組User的增刪改查操作,我們會寫一個UserController,然後其中有四個方法,對應User的增刪改查操作。好了,這部分沒什麼不同。我們來看看這四個方法的內部邏輯,比如增加用戶addUser方法,我們需要接受一些關於User的元信息,然後檢查這些元信息是否非空,是否合法等等,接着調用對應的service方法,然後返回一個執行正確或者執行錯誤的結果。再比如updateUser方法,我們仍然需要接受一個User的元信息,然後檢查這些元信息是否非空,是否合法等等,接着調用對應的service方法,然後返回一個執行正確或者執行錯誤的結果。
根據Donnot repeat yourself原則,我們自然想到:
1. 可否做一個抽象,將所有的參數合法性檢查放到一個地方來做,業務代碼僅僅需要指定傳入的參數類,框架會幫助檢查參數的合法性。如果不合法,直接返回異常。
2. 可否做一個抽象,將所有的返回值放到一個地方來做,業務代碼可以將任意的結果類返回,而無需關心該結果類和JsonString的轉化。
針對第一個問題,我們設計一種模式,讓業務developer每次實現一個URI,就新增一個對應該URI的Param類,然後框架會幫助檢查這個Param類。
針對第二個問題,我們設計一個包含泛型結果的類,讓業務developer每次僅僅需要將某一個處理結果類放入即可。
至於重用代碼的抽象,我們使用回調模式,來看代碼:
public interface CommonExecute<T extends BaseParam> {
HttpResult execute(T param);
}
public class CommonExecutor {
public static String execute(HttpServletRequest request, HttpServletResponse response, Class<? extends BaseParam> paramClass, CommonExecute commonExecute) {
//根據paramClass的原信息檢查參數的是否存在以及各式是否正確
BaseParam baseParam = null;
try {
baseParam = paramClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new SystemException(ErrorEnum.RUNTIME_EXCEPTION);
}
//inject the values into the baseParam
new ServletRequestDataBinder(baseParam).bind(request);
HttpResult result = commonExecute.execute(baseParam);
return JSON.toJSONString(result);
}
}
先來看第一個問題,有了上面的這兩個類以後,以後的Controller是這樣的:
@Resource(name = "userService")
IUserService userService;
@RequestMapping(value = CommonUrl.UrlConstant.GET_USER_BY_ACCOUNT, produces = CommonUtils.CONTENT_TYPE)
@ResponseBody
public String getUserByAccount(HttpServletRequest request, HttpServletResponse response) {
return CommonExecutor.execute(request, response, CommonUrl.GET_USER_BY_ACCOUNT.getParamClass(), new CommonExecute<GetUserByAccountParam>() {
@Override
public HttpResult execute(GetUserByAccountParam param) {
HttpResult<UserDTO> result = userService.getUserByAccount(param.getAccount());
return result;
}
});
}
public class GetUserByAccountParam extends BaseParam {
private String account;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
}
我們來看上面的代碼,當我需要寫一個getUserByAccount的API時,僅僅需要在對應的controller中添加一個如下的方法
public String getUserByAccount(HttpServletRequest request, HttpServletResponse response) {}
當我想添加比如名爲xxx方法的時候,僅僅需要添加同樣的方法:
public String xxx(HttpServletRequest request, HttpServletResponse response) {}
在方法體的內部,直接調用CommonExecutor的靜態execute方法,該方法有四個參數,前兩個參數是request和response參數,傳入皆可,無需感知。第三個方法是該方法對應的URI對應的參數類,可以看到,他是一個繼承自BaseParam子類的元類。說白了,就是你新添加的方法,需要傳入那些參數,把這些參數寫成一個類。
Class<? extends BaseParam>
第四個參數是一個實現了CommonExecute接口的匿名類,在這裏進行具體的業務處理。讀者可以看到,該類的一個傳入的參數就是你自定義的傳入參數類,只要用戶在發送請求的時候傳入和該參數類的字段域同名的參數,框架就會自動幫你注入到該傳入參數類中。這樣業務developer無需感知這個過程,在寫CommonExecute接口的匿名類時,直接使用自定義的參數類即可。
對於第二個問題,框架定義了一個HttpResult類:
public class HttpResult<T> implements Serializable {
private static final long serialVersionUID = -3404886040638951329L;
protected boolean success;
protected T model;
protected String msgCode;
protected String msgInfo;
public HttpResult() {
}
public HttpResult(boolean success, String msgCode, String msgInfo) {
this.success = success;
this.msgCode = msgCode;
this.msgInfo = msgInfo;
}
public static <T> HttpResult<T> successResult(T t) {
HttpResult<T> result = new HttpResult<T>();
result.setSuccess(true);
result.setModel(t);
return result;
}
public static <T> HttpResult<T> failedResult(String msgCode, String msgInfo) {
HttpResult<T> result = new HttpResult<T>();
result.setSuccess(false);
result.setMsgCode(msgCode);
result.setMsgInfo(msgInfo);
return result;
}
public static <T> HttpResult<T> failedResult(ErrorEnum errorEnum) {
HttpResult<T> result = new HttpResult<T>();
result.setSuccess(false);
if (errorEnum != null) {
result.setMsgCode(errorEnum.getErrCode());
result.setMsgInfo(errorEnum.getErrDesc());
}
return result;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public T getModel() {
return model;
}
public void setModel(T model) {
this.model = model;
}
public String getMsgCode() {
return msgCode;
}
public void setMsgCode(String msgCode) {
this.msgCode = msgCode;
}
public String getMsgInfo() {
return msgInfo;
}
public void setMsgInfo(String msgInfo) {
this.msgInfo = msgInfo;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
可以看見,HttpResult類是框架定義好的返回格式,在業務Developer實現CommonExecute接口的匿名類時,僅僅需要返回一個HttpResult類,框架會幫助把該HttpResult類格式化成爲Json返回給用戶,十分方便。
覺得文章不錯的話,別忘了關注我哦!
關於更將詳細的代碼,請參見筆者的github。點擊打開鏈接
筆者開設了一個知乎live,詳細的介紹的JAVA從入門到精通該如何學,學什麼?
提供給想深入學習和提高JAVA能力的同學,歡迎收聽https://www.zhihu.com/lives/93219220424868249