在Spring mvc中,注解@ModelAttribute是一個非常常用的注解,其功能主要在兩方面:
- 運用在參數上,會將客戶端傳遞過來的參數按名稱注入到指定對象中,並且會將這個對象自動加入ModelMap中,便於View層使用;
- 運用在方法上,會在每一個@RequestMapping標注的方法前執行,如果有返回值,則自動將該返回值加入到ModelMap中;
一般開發中,第一種用法居多,本次我將使用第二種用法以期節省controller層的一些代碼:
目前使用spring mvc開發的controller層方法一般類似於:
1 @RequestMapping("/{encodeId}/detail") 2 public String detail(ModelMap model, @PathVariable String encodeId) { 3 ..... 4 }
幾乎在每一個@RequestMapping標注的方法的參數中都會有 ModelMap model的參數,既然這是一個大概率事件,為什么不可以像注入request那樣,直接在類的開始使用@Resource進行自動注入呢?
另外一個,就是response,response也不能像request那樣進行自動注入。
類似的可能還有很多,既然這些都是controller層常用的代碼,如果能將其在一個basecontroller層自動注入,然后controller層繼承這個basecontroller,那樣就沒有必要再@RequestMapping標注的方法中寫上這些參數,使得參數個數減少,清晰。
我的思路正是使用@ModelAttribute注解,編寫一個basecontroller類,預定義一些項目中controller層常用的對象,如下:
1 @Resource 2 protected HttpServletRequest request; 3 4 protected ModelMap model; 5 6 protected HttpServletResponse response;
request不用解釋,可以直接使用@Resource直接注入,response和model的注入方式如下:
1 /** 2 * 設置response 3 * 4 * @param response 5 */ 6 @ModelAttribute 7 private final void initResponse(HttpServletResponse response) { 8 this.response = response; 9 } 10 11 /** 12 * 設置model 13 * 14 * @param model 15 */ 16 @ModelAttribute 17 private final void initModelMap(ModelMap model) { 18 this.model = model; 19 }
spring在執行@RequestMapping前會執行上述方法,spring會和平常一樣,每次請求重新生成一個model和response,然后注入到方法的參數中,這樣就變相在繼承了這個basecontroller的controller中自動注入了response和model,在這個controller層中再也不必每次寫ModelMap和response參數,整體代碼整潔了不少。
我在項目中這樣使用暫無問題,如果哪位高手知道這種做法會有弊端或者有更好的方法,求指正!
修正:
非常感謝eBusinessMan的提醒,確實有可能在spring mvc單例模式下會出現訪問對象不一致的情況,為了防止該問題,而又能保持這種代碼的簡潔性以及確保使用spring mvc性能問題不太嚴重,我決定使用ThreadLocal來處理。(后期驗證是否會出現該問題在本文進行補充)
(驗證結果:request采用spring的自動注入方式是線程安全的,response、model是不安全的,采用ThreadLocal可以解決該問題)
request對象不再使用注解自動注入(也可以繼續使用注解方式注入),而使用同response和model初始化的方式,取消request、response、model三個類變量,具體如下:
1 private static final ThreadLocal<HttpServletRequest> requestContainer = new ThreadLocal<HttpServletRequest>(); 2 3 private static final ThreadLocal<HttpServletResponse> responseContainer = new ThreadLocal<HttpServletResponse>(); 4 5 private static final ThreadLocal<ModelMap> modelContainer = new ThreadLocal<ModelMap>(); 6 7 /** 8 * 初始化response 9 * 10 * @param response 11 */ 12 @ModelAttribute 13 private final void initResponse(HttpServletResponse response) { 14 responseContainer.set(response); 15 } 16 17 /** 18 * 獲取當前線程的response對象 19 * 20 * @return 21 */ 22 protected final HttpServletResponse getResponse() { 23 return responseContainer.get(); 24 } 25 26 /** 27 * 初始化request 28 * 29 * @param request 30 */ 31 @ModelAttribute 32 private final void initRequest(HttpServletRequest request) { 33 requestContainer.set(request); 34 } 35 36 /** 37 * 獲取當前線程的request對象 38 * 39 * @return 40 */ 41 protected final HttpServletRequest getRequest() { 42 return requestContainer.get(); 43 } 44 45 /** 46 * 設置model 47 * 48 * @param model 49 */ 50 @ModelAttribute 51 private final void initModelMap(ModelMap model) { 52 modelContainer.set(model); 53 } 54 55 /** 56 * 獲取當前線程的modelMap對象 57 * 58 * @return 59 */ 60 protected final ModelMap getModelMap() { 61 return modelContainer.get(); 62 }