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