SpringMVC之ModelAttribute注解

ModelAttribute注解
 这个注解可以批注在方法上,也可以放在属性前

工作原理:
 1)、在某个方法上标注ModelAttribute注解,那么这个方法在目标方法执行前会优先执行
 2)、我们在ModelAttribute注解标注的方法入参中传入一个Map,
     这个map最终的类型BindingAwareModelMap,就是目标方法用的隐含模型的类型
 3)、只需要从数据库中获取到要修改的员工的原来值,并将它保存在map中,map的key一定要是后来
  目标方法入参的类名首字母小写
   3.1)、如果刚在在ModelAttribute注解方法运行的时候,map中保存的key不是按照类名首字母小写
      目标方法执行的时候就无法自动参照之前的值封装,
     我们可以在目标方法入参的对象上使用@ModelAttribute指定这个参数从隐含模型中获取的key
 4)、springMVC在执行目标方法封装参数的时候,会自动的将参数key在ModelAttribute方法中
  保存的值,与请求过来封装的值,进行自动对比,
  请求没有携带过来的值就会应用上之前在隐含模型中保存的值
 5)、封装数据是一个对比封装的过来,会对比隐含中已有的值和提交过来的值

 1)、标注在方法上
   这个handler的所有方法执行之前都会先执行ModelAttribute注解的方法
 2)、标注在方法的入参上
  指定这个参数封装是要参照的值从隐含模型中拿,使用value属性指定他的key
 3)、调试代码的时候发现
  1)、如果方法上的@ModelAttribute也有注解value
   说明value不是空串
   如果是空串,给隐含模型保存数据(方法的返回)的时候key是返回值类型首字母小写
   不是空串就是用指定的value作为key去隐含模型中保存@ModelAttribute方法的返回值
  2)、将@ModelAttribute执行完。将key-value保存进去
  3)、整个过程中@ModelAttribute使用的map,和目标方法的map都是一个对象
  4)、在方法上标注的ModelAttribute注解的value的作用
    是指定隐含模型中保存这个方法返回值的key
  5)、在参数上ModelAttribute注解value
    指定的key是指定从模型中获取数据的key
  6)、只要这两个是统一的我们就能自动封装

页面代码:

 <form action="update2" method="post">
  id:<input type="text" name="id"/><br/>
  name:<input type="text" name="name"/><br/>
  age:<input type="text" name="age"/><br/>
  email:<input type="text" name="email"/><br/>
  <input type="submit"/>
 </form> 
 
Controller代码:
@ModelAttribute
 public Employee testModelAttribute2(@RequestParam(value="id",required=false)Integer id){
  //在这里可以先查出这个数据库对应的记录
  if(id!=null){
   //查询数据库的计算出来的employee
   Employee employee = new Employee(3, "张三", 12, "aaa");
   employee.setAddress(new Address("北京市", "昌平区"));
   System.out.println("数据库中查出的记录:"+employee);
   return employee;
  }
  //我们可以将这个值,放在一个map中。然后目标方法在封装employee属性的时候
  //会自动的先从这个map中把刚才封装的值取出来,自动对比,自动封装
  return null;
 }
 @RequestMapping("/update2")
 public String updataEmployee2(@ModelAttribute Employee employee) {
  System.out.println("要保存到数据库的值是:"+employee);
  return "pages/success";
 } 


具体源码如下:

首先进入DispatcherServlet的doService方法:
DispatcherServlet.doService(HttpServletRequest, HttpServletResponse) line: 945	

	try {
		doDispatch(request, response);
	}

执行doDispatch方法:
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

调用AnnotationMethodHandlerAdapter.handle方法:
AnnotationMethodHandlerAdapter.handle(HttpServletRequest, HttpServletResponse, Object) line: 434

return invokeHandlerMethod(request, response, handler);

调用本类的invokeHandlerMethod方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
	Method handlerMethod = methodResolver.resolveHandlerMethod(request);
	ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	ExtendedModelMap implicitModel = new BindingAwareModelMap();

	//执行目标方法
	Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
	ModelAndView mav =
			methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
	methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
	return mav;
}

调用invokeHandlerMethod方法:
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
		NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

	//拿到要执行的目标方法
	Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
	try {
		boolean debug = logger.isDebugEnabled();
		for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
			Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
			if (attrValue != null) {
				implicitModel.addAttribute(attrName, attrValue);
			}
		}
		//遍历执行所有被ModelAttribute注解标注的方法
		for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
			//获得目标方法的全限定名
			Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
			//解析方法参数
			Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
			if (debug) {
				logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
			}
			//获得ModelAttribute注解的value值,如果没有返回"",有则返回,作为map里存放的key
			String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
			if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
				continue;
			}
			ReflectionUtils.makeAccessible(attributeMethodToInvoke);
			//ModelAttribute注解标注的方法的返回值作为map中保存的vlaue
			Object attrValue = attributeMethodToInvoke.invoke(handler, args);
			//如果ModelAttribute注解没有value值,则将方法的返回值类型小写作为map中存放的key
			if ("".equals(attrName)) {
				Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
				//获得方法的返回值类型小写,作为map中存放的key
				attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
			}
			if (!implicitModel.containsAttribute(attrName)) {
				//将属性名和属性值。添加到隐藏模型中
				implicitModel.addAttribute(attrName, attrValue);
			}
		}
		//到这里ModelAttribute注解标注的方法执行完毕
		//解析目标方法的参数
		Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
		if (debug) {
			logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
		}
		ReflectionUtils.makeAccessible(handlerMethodToInvoke);
		//这里才是执行目标方法的地方
		return handlerMethodToInvoke.invoke(handler, args);
	}
}


猜你喜欢

转载自blog.csdn.net/OnlyLove_longshao/article/details/78687244