1. 程式人生 > >springMVC原始碼分析[email protected]

springMVC原始碼分析[email protected]

這一篇部落格我們簡單的介紹一下ModelAttribute的使用和執行原理。

1、首先@ModelAttribute是使用在方法或者上的,當使用在方法上時其作用於本身所在的Controller,在訪問Controller中的所有請求時都會執行到@ModelAttribute所註解的方法。

@Controller
public class ModelAttributeController {
	
	@ModelAttribute
	public void init(Model model){
		model.addAttribute("test", "測試資訊");
	}
	
	@RequestMapping("/modelAttribute")
	public String modelAttribute(Model model){
		model.addAttribute("test1", "測試資訊1");
		return "modelAttribute";
	}
}
當訪問連線http://localhost/modelAttribute時會在頁面中看到test和test1的值。

2、@ModelAttribute也是可以作用於引數上的,我們在上面的程式碼中再新增一個作用於引數的引數。

@Controller
public class ModelAttributeController {
	
	@ModelAttribute
	public void init(Model model){
		model.addAttribute("test", "測試資訊");
	}
	
	@RequestMapping("/modelAttribute")
	public String modelAttribute(Model model,@ModelAttribute("test3")String test3){
		model.addAttribute("test1", "測試資訊1");
		model.addAttribute("test3", test3);
		return "modelAttribute";
	}
	
}
當訪問如下連結時就可以獲得如下資訊了。



3、@ModelAttribute註釋返回具體類,如下:

@Controller  
public class Hello2ModelController {  
      
    @ModelAttribute  
    public User populateModel() {    
       User user=new User();  
       user.setAccount("ray");  
       return user;  
    }    
      
    @RequestMapping(value = "/helloWorld2")    
    public String helloWorld(User user) {  
        user.setName("老王");  
       return "helloWorld.jsp";    
    }    
} 
也可以指定屬性名稱
@Controller  
public class Hello2ModelController {  
      
    @ModelAttribute(value="myUser")  
    public User populateModel() {    
       User user=new User();  
       user.setAccount("ray");  
       return user;  
    }    
    @RequestMapping(value = "/helloWorld2")    
    public String helloWorld(Model map) {    
       return "helloWorld.jsp";    
    }    
}  

總結:
@ModelAttribute一個具有如下三個作用:
①繫結請求引數到命令物件:放在功能處理方法的入參上時,用於將多個請求引數繫結到一個命令物件,從而簡化綁
定流程,而且自動暴露為模型資料用於檢視頁面展示時使用;
②暴露表單引用物件為模型資料:放在處理器的一般方法(非功能處理方法)上時,是為表單準備要展示的表單引用
物件,如註冊時需要選擇的所在城市等,而且在執行功能處理方法(@RequestMapping 註解的方法)之前,自動新增
到模型物件中,用於檢視頁面展示時使用;
③暴露@RequestMapping 方法返回值為模型資料:放在功能處理方法的返回值上時,是暴露功能處理方法的返回值為
模型資料,用於檢視頁面展示時使用。
為什麼@ModelAttribute註解的方法是作用於整個Controller的,實際上是在執行Controller的每個請求時都會執行@ModelAttribute註解的方法。

執行過程在RequestMappingHandlerAdapter中,每次執行Controller時會執行@ModelAttribute註解的方法

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		......
		//執行@ModelAttribute註解的方法
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		
		......
		//執行Controller中的方法
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		......
	
	}
initModel中會執行@ModelAttribute註解的方法
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
			throws Exception {

		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		mavContainer.mergeAttributes(sessionAttributes);
		//執行@ModelAttribute註解的方法
		invokeModelAttributeMethods(request, mavContainer);

		//方法執行結果的值放到mavContainer
		for (String name : findSessionAttributeArguments(handlerMethod)) {
			if (!mavContainer.containsAttribute(name)) {
				Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
				if (value == null) {
					throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
				}
				mavContainer.addAttribute(name, value);
			}
		}
	}

在invokeModelAttributeMethods中會判斷方法上是否被@ModelAttribute註解,如果是則會執行這個方法,並將返回值放到mavContainer中

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
			throws Exception {

		while (!this.modelMethods.isEmpty()) {
			InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
			//判斷方法是否被@ModelAttribute註解
			String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
			if (mavContainer.containsAttribute(modelName)) {
				continue;
			}
			//執行被@ModelAttribute註解的方法
			Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
			//返回值放到mavContainer
			if (!attrMethod.isVoid()){
				String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
				if (!mavContainer.containsAttribute(returnValueName)) {
					mavContainer.addAttribute(returnValueName, returnValue);
				}
			}
		}
	}

總結:這邊部落格簡單地介紹了一下@ModelAttribute的用法,當其註解方法時,這個方法在每次訪問Controller時都會被執行,其執行到的原理就是在每次執行Controller時都會判斷一次,並執行@ModelAttribute的方法,將執行後的結果值放到mavContainer中,現在看來其實現機制也還是比較容易理解的。