上面讲述的是从 JSP的 a 标签发出请求到获取视图页面。该篇简要描述一下从填写表单页面到后台成功响应的过程。
常见参数解析器:
JSP页面
这里使用了form标签:
<form:form action="${pageContext.request.contextPath }/emp" method="POST"
modelAttribute="employee">
<c:if test="${employee.id == null }">
<!--【 path 属性对应 html 表单标签的 name 属性值】 -->
LastName: <form:input path="lastName"/>
</c:if>
<!-- 当id不为空的时候,才将请求由post 转为put请求,适应修改操作 -->
<c:if test="${employee.id != null }">
<form:hidden path="id"/>
<input type="hidden" name="_method" value="PUT"/>
</c:if>
<br><br>
Email: <form:input path="email"/>
<br><br>
<%
Map<String, String> genders = new HashMap();
genders.put("1", "Male");
genders.put("0", "Female");
request.setAttribute("genders", genders);
%>
Gender:
<br><br>
<form:radiobuttons path="gender" items="${genders}" delimiter=" " />
<br><br>
<!--itemValue 为集合中bean的属性,集合为bean的集合 -->
Department:
<form:select path="department.id" items="${departments }" itemLabel="departmentName" itemValue="id">
<form:option value="0">请选择</form:option>
</form:select>
<br><br>
Birth: <form:input path="birth"/>
<br><br>
Salary: <form:input path="salary"/>
<br><br>
<input type="submit" value="Submit"/>
</form:form>
后台code
- code1( @ModelAttribute注解的方法,在每个方法响应前先执行该房屋):
@ModelAttribute
public void getEmployee(@RequestParam(value="id1",required=false) Integer id,Map<String, Object> map){
if(id != null){
map.put("employee", employeeDao.get(id));
}
System.out.println("@ModelAttribute 方法执行");
}
- code2:
@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid Employee employee, Errors result,
Map<String, Object> map){
System.out.println("save: " + employee);
if(result.getErrorCount() > 0){
System.out.println("出错了!");
//校验错误信息
for(FieldError error:result.getFieldErrors()){
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
//若验证出错, 则转向定制的页面
map.put("departments", departmentDao.getDepartments());
return "input";
}
employeeDao.save(employee);
return "forward:/emps";
//非具体视图,而是再次转发到String list()最后返回list
}
- code3:
@RequestMapping("/emps")
public String list(Map<String, Object> map){
map.put("employees", employeeDao.getAll());
System.out.println("list method execute...");
return "list";
}
下面开始根据源码追溯整个流程。
【1】DispatcherServlet.doDispatcher()
(1)根据handlerMapping获取HandlerExecutionChain:
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
/*根据handlerMapping拿到handlerExecutionChain*/
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
(2)通过HandlerExecutionChain获取handler从而得到handlerAdapter
HandlerExecutionChain mappedHandler =
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- 【getHandlerAdapter()】
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
- POST 方法,跳过if判断,直接执行拦截器前置方法;
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
【2】RequestMappingHandlerAdapter.handle!!!
go on and go on…
- HandlerMethod handlerMethod通过HandlerExecutionChain获得;
来到办实事的方法ModelAndView invokeHandleMethod()
:
private ModelAndView invokeHandleMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
}
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
- 【ModelAndView handleInternal】:
protected final ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandleMethod(request, response, handlerMethod);
}
}
}
return invokeHandleMethod(request, response, handlerMethod);
【第一步:创建数据绑定工厂:】
- 此时绑定的方法为空;
- binderFactory类型为ServletRequestDataBinderFactory;
initializer基本信息如下:
【第二步:创建modelFactory】
- attributeMethods 只有一个方法–getEmployee;
- 数据绑定工厂–上面刚建立的;
- sessionAttributeHandler!!!判断你的controller有没有使用@SessionAttributes注解;
【第三步:创建请求映射方法对象】
- 参数为物理映射的方法(save)和数据绑定工厂;
.
- 方法如下:
private ServletInvocableHandlerMethod createRequestMappingMethod(
HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
ServletInvocableHandlerMethod requestMethod;
requestMethod = new ServletInvocableHandlerMethod(handlerMethod);
//包装成ServletInvocableHandlerMethod
requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//设置参数解析器
requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
//设置方法返回值处理器
requestMethod.setDataBinderFactory(binderFactory);
//为方法对象绑定 binderFactory
requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//设置参数发现者
return requestMethod;
}
如此之后,下面才可以使用该方法对象进行解析。
【第四步:创建MV容器,并尝试将request中的modelmap放入mv容器】
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
- defaultModel:BindingAwareModelMap
- redirectModel:null
- requestHandled:false(注意该属性,在最后一步获得mv时将会用到)
- sessionStatus:SimpleSessionStatus(有属性complete,同样在获得mv时会用到)
【第五步:initModel!】
①
Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(attributesInSession);
//看session中有没有model,有了copy到mav容器
invokeModelAttributeMethods(request, mavContainer);
//调用@ModelAttribute注解的方法
②
注释:先解析方法的参数值,然后调用该方法,最后拿到返回值。
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
/*注意:在这个方法里,解析了@ModelAttribute注解的方法,为参数赋值,并进行了数据格式化!!!*/
- 【解析目标方法参数】:
- 【方法getMethodArgumentValues】:
/**
* Get the method argument values for the current request.
*/
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*Returns the method parameters for this handler method. 返回parameters对象数组,每一个对象包含了对应的方法参数类型和其他信息 刚开始对象里面方法参数类型为null*/
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
/*Initialize parameter name discovery for this method parameter. */
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
/*Determine the target type for the given generic parameter type.---确定给定泛型参数类型的目标类型。*/
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
/*Attempt to resolve a method parameter from the list of provided argument values.*/
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
return args;
}
- 【方法getMethodParameters()】:
/**
* Returns the method parameters for this handler method.
*/
public MethodParameter[] getMethodParameters() {
return this.parameters;
}
此时的参数解析器:
- RequestParamMethodArgumentResolver 【简单参数类型默认使用该解析器】;
方法如下:
@Override
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
Class<?> paramType = parameter.getParameterType();
//获取namedValueInfo,属性name对应方法参数的key
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
//这里使用RequestParamMethodArgumentResolver进行解析;
Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required) {
handleMissingValue(namedValueInfo.name, parameter);
}
arg = handleNullValue(namedValueInfo.name, arg, paramType);
}
else if ("".equals(arg) && (namedValueInfo.defaultValue != null)) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
//我要创建并初始化binder对象,尝试放conversionService和validator等进去---注意此时binder的bindingresult为null!!!
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
//如果必要还会进行数据格式化-Convert the value to the required type (if necessary from a String).
arg = binder.convertIfNecessary(arg, paramType, parameter);
}
//看这里,处理解析得到的value--有两个方法
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
- 【方法getNamedValueInfo】:
/**
* Obtain the named value for the given method parameter.
*/
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
//首先尝试获取
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
//如果为空则创建,如方法参数没有显示声明key
if (namedValueInfo == null) {
namedValueInfo = createNamedValueInfo(parameter);
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
**这里需要注意的事,namedValueInfo的name属性即为参数对应的key:
① 如果用@RequestParam(key)显示声明,那么参数对应的key为括号内显示声明的;
② 如果没有显示声明,那么为参数本身;
如下图所示(以前均为未使用RequestParam显示声明的):
name = “Id”;
name=“ID”
RequestParamMethodArgumentResolver.resolveName
-
- 普通参数解析器的解析过程
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
Object arg;
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
-- 先判断是不是文件
if (MultipartFile.class.equals(parameter.getParameterType())) {
assertIsMultipartRequest(servletRequest);
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
arg = multipartRequest.getFile(name);
}
else if (isMultipartFileCollection(parameter)) {
assertIsMultipartRequest(servletRequest);
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
arg = multipartRequest.getFiles(name);
}
else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
assertIsMultipartRequest(servletRequest);
arg = servletRequest.getPart(name);
}
else if (isPartCollection(parameter)) {
assertIsMultipartRequest(servletRequest);
arg = new ArrayList<Object>(servletRequest.getParts());
}
else {
arg = null;
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
--不是文件,那么 arg 此时为null
if (arg == null) {
-- 注意这里,使用了我们最熟悉的获取参数的方式!!!!
String[] paramValues = webRequest.getParameterValues(name);
if (paramValues != null) {
arg = paramValues.length == 1 ? paramValues[0] : paramValues;
}
}
}
return arg;
}
解析得到的参数值:
方法-PathVariableMethodArgumentResolver.handleResolvedValue
- 该次没有调用该方法
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest request) {
String key = View.PATH_VARIABLES;
int scope = RequestAttributes.SCOPE_REQUEST;
Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope);
if (pathVars == null) {
pathVars = new HashMap<String, Object>();
request.setAttribute(key, pathVars, scope);
}
pathVars.put(name, arg);
}
解析第二个参数,参数类型为map
- 此时解析器为.MapMethodProcessor
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler
方法如下:
-
- map解析器就是返回mavContainer里面的model!!!
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
//就是一句话,返回mav里面的model
return mavContainer.getModel();
}
mavContainer.getModel();
/**
* Return the model to use: the "default" or the "redirect" model.
* <p>The default model is used if {@code "redirectModelScenario=false"} or
* if the redirect model is {@code null} (i.e. it wasn't declared as a
* method argument) and {@code ignoreDefaultModelOnRedirect=false}.
*/
public ModelMap getModel() {
//这里的defaultModel为BindingAwareModelMap
if (useDefaultModel()) {
return this.defaultModel;
}
else {
return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
}
}
@ModelAttribute注解的方法有两个参数,第一个为 Integer id,第二个为map。在解析第一个id时,使用了binder进行参数格式化转换。但是不用校验,故而BindingResult为null。解析第二个参数map类型时,并未用到binder而是直接返回了mavContainer.getModel()
此时的ModelAndViewContainer: View is [null]; default model {}
至此 @ModelAttribute注解的方法结束,mav容器有了初步填充!!!
initModel方法结束!!!initModel方法结束!!!initModel方法结束!!!
开始执行真正的目标方法!!!开始执行真正的目标方法!!!开始执行真正的目标方法!!!
【第六步:处理真正的目标方法-save】
- 该方法有三个参数:
Employee employee, Errors result, Map<String, Object> map
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
- ServletInvocableHandlerMethod.invokeAndHandle
public final void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 在这里调用父类(.InvocableHandlerMethod)的invokeForRequest
//这个方法意思是:解析完目标方法的参数值后调用其方法,并拿到方法的返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
//在这里处理方法的返回值!!!!
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
【第七步:使用父类的方法解析参数】
public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//在这里拿到解析得到的参数值数组
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(this.getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
/*在这里调用真正的物理目标方法 --第一次为save */
Object returnValue = invoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
//把方法的返回值,返回上级方法
return returnValue;
}
【第八步:解析参数-循环遍历解析参数】
①
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
② —拿到对应的参数解析器
@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
//根据parameter,拿到对应的resolver!!!
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
第一个参数为Employee employee,参数类型和resolver如下所示:
- 参数解析器为:ServletModelAttributeMethodProcessor!!!
【第九步:.ModelAttributeMethodProcessor.resolveArgument】
- 解析参数类型为Employee的参数
- 注意该类的resolveArgument与上一个运行流程示例不同!
- 数据的绑定,格式化以及校验都在这个方法里面
@Override
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory)
throws Exception {
//根据MethodParameter parameter对象 拿到 name(key)
String name = ModelFactory.getNameForParameter(parameter);
//判断mav里面有没有,没有就根据参数类型创建一个空的对象
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
//创建binder对象--不同解析器的解析参数方法此处会有不同!!!
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
//如果target不为空,则进行绑定和数据格式化
bindRequestParameters(binder, request);
//调用注册的validators,进行校验
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
① 创建attribute
@Override
protected final Object createAttribute(String attributeName,MethodParameter parameter,WebDataBinderFactory binderFactory,NativeWebRequest request) throws Exception {
//根据name尝试从request中取值
String value = getRequestValueForAttribute(attributeName, request);
if (value != null) {
Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
if (attribute != null) {
return attribute;
}
}
//调用父类的方法
return super.createAttribute(attributeName, parameter, binderFactory, request);
}
【第十步:创建binder对象】
- 此时仍在ServletModelAttributeMethodProcessor.resolveArgument
- 此时name为employee;
- attribute为空的Employee对象;
- 根据name、attribute和request创建binder对象;
【 何为binder对象???何为binder对象???】
- 以下为binder对象的基本属性:
一般包括objectName(key),target(value),bindingResult(校验结果),conversionService(数据转换和格式化)以及validators(校验器)等
看errors与BindingResult之间的家庭族谱:
看WebDataBinder WebDataBinder extends DataBinder
注释:
Special DataBinder for data binding from web request parameters to JavaBean objects.
--将请求参数绑定到JavaBean 对象(这就是主要功能!!!)
--因为方法参数Employee employee 为Java bean ,所以需要使用binder!!!
Designed for web environments, but not dependent on the Servlet API;
serves as base class for more specific DataBinder variants,
such as org.springframework.web.bind.ServletRequestDataBinder.
- 创建binder方法
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
下面就明白了,就是把请求中的表单参数一个个绑定到java bean对应的属性上!!!
① 具体创建binder对象方法
@Override
public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
throws Exception {
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
initBinder(dataBinder, webRequest);
return dataBinder;
}
- 初始化binder时,设置validator和conversionService:
binder初始化完毕,属性如下:
此时属性bindingResult仍旧为null
【第十一步:绑定请求参数值】
①
bindRequestParameters(binder, request);
②–把请求参数值绑定到java bean object上面(Employee employee)
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
servletBinder.bind(servletRequest);
}
③ --真正的bind方法
class:ServletRequestDataBinder
public void bind(ServletRequest request) {
//注意这里!!!说明了mpvs从哪里来!!!!
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
/*Extension point that subclasses can use to add extra bind values for a request. Invoked before doBind(MutablePropertyValues). The default implementation is empty.*/
//从请求中获取uri的 key:value,添加到mpvs里面。
addBindValues(mpvs, request);
//执行真正的绑定--将mpvs里面的key:value,一一绑定到Java bean
doBind(mpvs);
}
- 此时的mpvs的 elementData长度为六,即employee的六个属性
-
其中,属性对应的值如下:
-
- 每一个元素是个map对象,其中包含了 property以及对应的value!!!
④
注释如下:Merge URI variables into the property values to use for data binding.
—就是拿URL里面的参数放到mpvs里面!
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
Map<String, String> uriVars = (Map<String, String>) request.getAttribute(attr);
if (uriVars != null) {
for (Entry<String, String> entry : uriVars.entrySet()) {
if (mpvs.contains(entry.getKey())) {
logger.warn("Skipping URI variable '" + entry.getKey()
+ "' since the request contains a bind value with the same name.");
}
else {
mpvs.addPropertyValue(entry.getKey(), entry.getValue());
}
}
}
}
好了,更新完mpvs了,URL里面的参数也拿到了!
⑤ 真正开始绑定
- 为参数Employee employee赋值;
- 此时的class:public class WebDataBinder extends DataBinder 。
@Override
protected void doBind(MutablePropertyValues mpvs) {
/*Check the given property values for field defaults, i.e. for fields that start with the field default prefix.
The existence of a field defaults indicates that the specified value should be used if the field is otherwise not present.
*/
checkFieldDefaults(mpvs);
/*Check the given property values for field markers, i.e. for fields that start with the field marker prefix.
The existence of a field marker indicates that the specified field existed in the form. If the property values do not contain a corresponding field value, the field will be considered as empty and will be reset appropriately.
*/
checkFieldMarkers(mpvs);
//检测完了,开始调用父类的boBind
super.doBind(mpvs);
}
⑥ 父类的doBind
此时class:DataBinder implements PropertyEditorRegistry, TypeConverter
protected void doBind(MutablePropertyValues mpvs) {
/*Check the given property values against the allowed fields, removing values for fields that are not allowed.*/
checkAllowedFields(mpvs);
/*Check the given property values against the required fields, generating missing field errors where appropriate.*/
checkRequiredFields(mpvs);
//赋值!!!
/*Apply given property values to the target object.
--为目标对象赋值(target对象)
Default implementation applies all of the supplied property values as bean property values. By default, unknown fields will be ignored.*/
applyPropertyValues(mpvs);
}
⑦ 重头戏开始 void applyPropertyValues
—还记得吧,此时的target(即前面根据目标方法参数类型创建的attribute)还为空的Employee对象
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.开始为target 对象 赋值啦!!!另外注意,开始用到bindingResult了!
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}
⑧ 首先你要获取getPropertyAccessor()
方法注释:Return the underlying PropertyAccessor of this binder's BindingResult.
protected ConfigurablePropertyAccessor getPropertyAccessor() {
//从bindingResult里面获取PropertyAccessor
return getInternalBindingResult().getPropertyAccessor();
}
⑨ getInternalBindingResult()
- 初始化bindingResult!!!
方法注释:Return the internal BindingResult held by this DataBinder, as AbstractPropertyBindingResult.
protected AbstractPropertyBindingResult getInternalBindingResult() {
if (this.bindingResult == null) {
//如果bindingResult为空,则进行初始化!!!
initBeanPropertyAccess();
}
return this.bindingResult;
}
org.springframework.validation.BindingResult
General interface that represents binding results.
--呈现绑定结果的通用接口。
Extends the interface for error registration capabilities,
--扩展了该接口的错误注册能力
allowing for a Validator to be applied, and adds binding-specific analysis and model building.
--允许校验器使用,并增加了特定绑定分析和model 构建
Serves as result holder for a DataBinder, obtained via the DataBinder.getBindingResult() method.
--可以通过DataBinder.getBindingResult() 方法获得该对象(作为result holder)。
BindingResult implementations can also be used directly, for example to invoke a Validator on it (e.g. as part of a unit test).
/*也就是说,会把model放进去,并对target--JavaBean object 进行校验,校验结果Errors放进bindingResult!!!*/
⑩ void initBeanPropertyAccess()
方法注释如下:Initialize standard JavaBean property access for this DataBinder. This is the default; an explicit call just leads to eager initialization.
–为DataBinder初始化 属性(javaBean property)访问通道;该方式是默认的,如果想更早,也可以显示调用 。
public void initBeanPropertyAccess() {
/*如果bindingResult为null,就创建*/
Assert.state(this.bindingResult == null,
"DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
/*Default implementation of the Errors and BindingResult interfaces, for the registration and evaluation of binding errors on JavaBean objects.
/*Errors或者bindingResult接口的默认实现类,实现JavaBean objects的 注册和评估错误绑定能力*/
Performs standard JavaBean property access, also supporting nested properties.
/*Performs standard JavaBean property access, also supporting nested properties.*/
Normally, application code will work with the Errors interface or the BindingResult interface.
/*通常,应用代码会使用Errors或者BindingResult接口工作*/
A DataBinder returns its BindingResult via DataBinder.getBindingResult().
/*一个数据绑定对象通过getBindingResult()方法返回它的BindingResult*/
*/
this.bindingResult = new BeanPropertyBindingResult(
getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
/*放进去target 和objectName!!!*/
if (this.conversionService != null) {
this.bindingResult.initConversion(this.conversionService);
/* 初始化Conversion!!!!*/
}
}
- 如下图所示,target对象和objectName对象从databinder中获取【同时表明,如果在】:
-
【BeanPropertyBindingResult 】
-
有属性target;
-
无属性objectName,从父类AbstractPropertyBindingResult拿;
@SuppressWarnings("serial")
public class BeanPropertyBindingResult extends AbstractPropertyBindingResult implements Serializable {
//有属性target
private final Object target;
private final boolean autoGrowNestedPaths;
private final int autoGrowCollectionLimit;
//beanWrapper
private transient BeanWrapper beanWrapper;
/**
* Creates a new instance of the {@link BeanPropertyBindingResult} class.
* @param target the target bean to bind onto
* @param objectName the name of the target object
*/
public BeanPropertyBindingResult(Object target, String objectName) {
this(target, objectName, true, Integer.MAX_VALUE);
}
/**
* Creates a new instance of the {@link BeanPropertyBindingResult} class.
* @param target the target bean to bind onto
* @param objectName the name of the target object
* @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
* @param autoGrowCollectionLimit the limit for array and collection auto-growing
*/
public BeanPropertyBindingResult(Object target, String objectName, boolean autoGrowNestedPaths, int autoGrowCollectionLimit) {
//调用父类的该方法。。。直到调用父类AbstractBindingResult的方法
super(objectName);
this.target = target;
this.autoGrowNestedPaths = autoGrowNestedPaths;
this.autoGrowCollectionLimit = autoGrowCollectionLimit;
}
//...
现在获得了bindingResult,属性如下:
- 属性有objectName,target、errors(存放校验错误信息)、conversionService以及下面重要的**beanWrapper(BeanWrapperImpl)**等。
(11)—看一下getPropertyAccessor()的返回结果
class:beanPropertyBindingResult
@Override
public final ConfigurablePropertyAccessor getPropertyAccessor() {
if (this.beanWrapper == null) {
this.beanWrapper = createBeanWrapper();
this.beanWrapper.setExtractOldValueForEditor(true);
this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
}
//看,最终返回的结果!!!
return this.beanWrapper;
}
看到上面,return this.beanWrapper !使用该对象进行属性设置!
回到⑦ void applyPropertyValues
开始为属性(propertyValues)赋值
如下图所示:
- 如下图所示:把表单参数值,一一对应,赋值给propertyValue!
下面开始分析赋值流程
⑿ void setPropertyValues
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
//循环遍历赋值循环遍历赋值循环遍历赋值
for (PropertyValue pv : propertyValues) {
try {
// This method may throw any BeansException, which won't be caught
// here, if there is a critical failure such as no matching field.
// We can attempt to deal only with less serious exceptions.
//调用具体赋值方法
setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
if (!ignoreUnknown) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (NullValueInNestedPathException ex) {
if (!ignoreInvalid) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (PropertyAccessException ex) {
if (propertyAccessExceptions == null) {
propertyAccessExceptions = new LinkedList<PropertyAccessException>();
}
propertyAccessExceptions.add(ex);
}
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray =
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
}
}
(13) void setPropertyValue
- 对具体属性赋值;
- 期间会发生数据类型转换(比如gender的参数值为string,但是类型为Integer);
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
BeanWrapperImpl nestedBw;
try {
//拿到BeanWrapperImpl
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
if (nestedBw == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
//看这里!!!!为对应属性赋值!!!
nestedBw.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
进入这个setPropertyValue方法
- 赋值期间可能发生数据类型转换
BeanWrapperImpl.setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv)
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
//...
valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd, new TypeDescriptor(property(pd)));
//...
//使用反射,为object 对应属性赋值(使用其set方法)
writeMethod.invoke(this.object, value);
}
-
- 继续遍历赋值…
- 为Gender赋值示例如下:
此时回到方法:void setPropertyValue((PropertyTokenHolder tokens, PropertyValue pv) )
class:void org.springframework.beans.BeanWrapperImpl
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
//...
writeMethod.invoke(this.object, value);
//...
使用反射进行赋值:
- writeMethod :setGender;
- object:Employee;
- value:0;
如下图所示:
为gender赋值
- 为gender赋值的时候发生了数据类型转换
此时回到方法void setPropertyValue(PropertyValue pv)
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
BeanWrapperImpl nestedBw;
try {
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
if (nestedBw == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
nestedBw.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
回到方法void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
try {
// This method may throw any BeansException, which won't be caught
// here, if there is a critical failure such as no matching field.
// We can attempt to deal only with less serious exceptions.
setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
if (!ignoreUnknown) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (NullValueInNestedPathException ex) {
if (!ignoreInvalid) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (PropertyAccessException ex) {
if (propertyAccessExceptions == null) {
propertyAccessExceptions = new LinkedList<PropertyAccessException>();
}
propertyAccessExceptions.add(ex);
}
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray =
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
}
}
赋值完毕!!! 赋值完毕!!! 赋值完毕!!! 赋值完毕!!!
回到方法Object resolveArgument
@Override
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory)
throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
//已经使用bindingResult里面的beanWrapper为Java bean object绑定了请求中的参数--Java bean初始化完毕!!!
bindRequestParameters(binder, request);
//开始校验!!!
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
开始校验!!!开始校验!!! 开始校验!!!开始校验!!!
查看此时的binder:
- binder:ExtendedServletRequestDataBinder;
- bindingResult
- 完整的binder如下:
【请注意一个细节】:
binder中的target的id与 BindingResult中的target的id 一致!!!这说明各自属性引用的target对象是同一个!!
因为target是一个对象,在创建bindingResult使用了binder的target进行创建。此处应用的传递是引用传递,那么,BindingResult对target进行操作的时候,binder中的target也会改变!!!
校验方法void validateIfApplicable(WebDataBinder binder, MethodParameter parameter)
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation annot : annotations) {
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(annot);
binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
break;
}
}
}
真正开始校验真正开始校验真正开始校验真正开始校验!!!
validata方法注释:
Validate the supplied target object, which must be of a type of Class for which the supports(Class) method typically returns true.
/*校验target*/
The supplied errors instance can be used to report any resulting validation errors.
/*Erros 的子类实例可以存储校验错误*/
/* 该方法意思是校验target对象,并且把错误放在bindingresult里面!!!*/
Errors 接口:
/* Stores and exposes information about data-binding and validationerrors for a specific object.
*/
public interface Errors {}
BindingResult接口:
/**
* General interface that represents binding results. Extends the
* {@link Errors interface} for error registration capabilities,
* allowing for a {@link Validator} to be applied, and adds
* binding-specific analysis and model building.
*
* <p>Serves as result holder for a {@link DataBinder}, obtained via
* the {@link DataBinder#getBindingResult()} method. BindingResult
* implementations can also be used directly, for example to invoke
* a {@link Validator} on it (e.g. as part of a unit test).
*/
public interface BindingResult extends Errors {}
public void validate(Object... validationHints) {
for (Validator validator : getValidators()) {
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints);
}
else if (validator != null) {
validator.validate(getTarget(), getBindingResult());
}
}
}
- getTarget():从binder中获取target;
- getBindingResult():从binder中获取bindingResult;
validate方法如下
@Override
@SuppressWarnings("rawtypes")
public void validate(Object target, Errors errors, Object... validationHints) {
Set<Class> groups = new LinkedHashSet<Class>();
if (validationHints != null) {
for (Object hint : validationHints) {
if (hint instanceof Class) {
groups.add((Class) hint);
}
}
}
processConstraintViolations(
this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors);
}
processConstraintViolations方法说明如下图所示:
校验完毕!!!校验完毕!!!校验完毕!!!校验完毕!!!
- 此时的binder与bindingResult:**
- bindingResult中errors为0
回到解析参数的方法-Object resolveArgument
—终于回来了!!!
@Override
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory)
throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, request);
validateIfApplicable(binder, parameter);
/*从这里开始执行,看校验有没有错误!!!*/
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
/*Add resolved attribute and BindingResult at the end of the model*/
/*从BindingResult中拿到model---因为校验错误信息也在bindingResult里面;之所以不从binder对象里面获取,是因为前者更方便,一次将普通model与errors都获取出来*/
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
/*{employee=Employee [id=null, lastName=aa, [email protected], gender=0, department=Department [id=101, departmentName=null], birth=null, salary=null],
//第一个参数结束org.springframework.validation.BindingResult.employee=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
//如果参数有result的子类型,可以拿到校验的error
*/
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
/*这两个方法的目的是,如果原先mav有和bindingResultModel同样的key,就移除掉;然后重新添加bindingResultModel于mav容器中*/
return binder.getTarget();
/*返回target!!!注意,并没有返回Error,需要目标方法参数中注册Error errors*/
}
- 从这里获取bindingResultModel
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
- 具体BeanPropertyBindingResult.getModel方法:
代码注释已经很清晰了,所以该语句过后,代码中的model涵盖了普通model和errors(key为MODEL_KEY_PREFIX + getObjectName())
@Override
public Map<String, Object> getModel() {
Map<String, Object> model = new LinkedHashMap<String, Object>(2);
// Mapping from name to target object.
model.put(getObjectName(), getTarget());
// Errors instance, even if no errors.
model.put(MODEL_KEY_PREFIX + getObjectName(), this);
return model;
}
- 父类为AbstractBindingResult:
AbstractBindingResult extends AbstractErrors implements BindingResult, Serializable{}
此时返回的target !!!
回到HandlerMethodArgumentResolverComposite.resolveArgument
/**
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/
@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
再次返回,回到Object[] getMethodArgumentValues
第一个参数解析结束!!!
该此过程是先以Employee参数类型构建了一个空的Employee对象,使用binder将表单中的参数绑定到空的Employee对象的对应属性上,进行数据格式化和校验。。。最终返回binder的target对象!!!!即,属性赋值后的Employee!!!
第二参数开始解析!!!
- 第二个参数类型为Errors,解析器如下:
-
ErrorsMethodArgumentResolver的解析方法如下
-
即拿到保存校验错误的model.get(key)(key为MODEL_KEY_PREFIX+objectName);
org.springframework.validation.BeanPropertyBindingResult: 0 errors
@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
/*从mav中获取model*/
ModelMap model = mavContainer.getModel();
//尝试获取errors_map
if (model.size() > 0) {
int lastIndex = model.size()-1;
String lastKey = new ArrayList<String>(model.keySet()).get(lastIndex);
if (lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return model.get(lastKey);
}
}
throw new IllegalStateException(
"An Errors/BindingResult argument is expected to be declared immediately after the model attribute, " +
"the @RequestBody or the @RequestPart arguments to which they apply: " + parameter.getMethod());
}
}
第二个参数解析结束!!!
第三个参数开始解析!!!
- 因为第三个参数是map类型,故解析器为MapMethodProcessor
第三个参数解析结束!!!
- 第三个参数默认类型为BindingAwareModelMap,有两个对象,一个Employee,另外一个对象为如下形式:
org.springframework.validation.BindingResult.employee=org.springframework.validation.BeanPropertyBindingResult: 0 errors}]
同时可以说明,bindingresult 数据也是放在了modelmap中,Employee数据也是放在了modelmap中,故第三个参数类型为map时,将会获得其所有model!!!—因为其最终都对应一个defaultModel–BindingAwareModelMap!!
至此,参数全部解析结束!!!
返回到Object invokeForRequest
public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(this.getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
Object returnValue = invoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
- 此时args如下:
执行物理目标方法 save()!
- 注意:return “forward:/emps”
- 拿到返回值:
回到void invokeAndHandle()
方法
① 拿到返回值;
② 处理返回值;
开始处理返回值
此时的handler:ViewNameMethodReturnValueHandler
① 拿到处理返回值对应的handler;
@Override
public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
② 使用对应的handler处理返回值;
public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
if (returnValue == null) {
return;
}
else if (returnValue instanceof String) {
//转换为string
String viewName = (String) returnValue;
//为mav设置视图名字为mav设置视图名字为mav设置视图名字
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
此时的mav:
ModelAndViewContainer: reference to view with name 'forward:/emps';
default model {employee=Employee [id=1006, lastName=aa, [email protected], gender=0, department=Department [id=101, departmentName=D-AA], birth=null, salary=null],
org.springframework.validation.BindingResult.employee=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
处理完目标方法和返回值了!!!处理完目标方法和返回值了!!!
回到方法ModelAndView invokeHandleMethod
可以决定最终返回的ModelAndView了!!!
- 在多个地方为mav赋值model;
- 在执行物理目标方法时,为mav设置了viewName;
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//先更新了modelFactory里面的model
/*Synchronize model attributes with the session. Add BindingResult attributes where necessary.*/
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
//重新获取model
ModelMap model = mavContainer.getModel();
//根据重新获取的model,生成新的mav;
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
//如果mav没有视图关联,就设置一个view;
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
//返回mav
return mav;
}
方法void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer)
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
//如果session失效了,移除sessionAttributes---在这里使用了sessionStatus属性!!!
if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
/*Store a subset of the given attributes in the session. Attributes not declared as session attributes via @SessionAttributes are ignored.*/
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
}
//如果请求不是直接处理的---在这里使用了requestHandled属性!!!
if (!mavContainer.isRequestHandled()) {
//注意这里--Add BindingResult attributes to the model for attributes that require it.
updateBindingResult(request, mavContainer.getModel());
}
}
方法updateBindingResult方法updateBindingResult方法updateBindingResult
方法注释:Add BindingResult attributes to the model for attributes that require it.
解释:为容器中的(key : value)对象,添加必要的BindingResult!!!如Employee,则需要BindingResult;Map等简单类型,则不需要!!!
----这就是最后更新model的意义所在。例如方法参数中有Errors类型的参数,将直接从mav.getModel.get(bindingResultKey)获取其对应的value
—可能会说,解析参数类型为Employee时,已经在defaultModel放入了 普通key value对象和BindingResult;
但是解析完参数的时候,就调用了目标物理方法,model中的值可能改变,如手动添加(“user”,user)对象进入model,而Java bean object 一般需要校验需要BindingResult!!故需要重新更新,以使视图拿到最新的mav进行渲染!!!
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
//拿到 key的列表
List<String> keyNames = new ArrayList<String>(model.keySet());
//循环遍历每一个name
for (String name : keyNames) {
//从model拿到值
Object value = model.get(name);
//Whether the given attribute requires a BindingResult in the model.如类似于Employee的参数就判断为需要
if (isBindingCandidate(name, value)) {
//拼接获得bindingResultKey,带有其name属性
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
//如果model没有这个bindingResultKey对应的属性---添加!!!有了就跳过
if (!model.containsAttribute(bindingResultKey)) {
//根据request、value、name创建databinder 以使dataBinder.getBindingResult()使用!!
WebDataBinder dataBinder = binderFactory.createBinder(request, value, name);
//又更新了一次model!!!
model.put(bindingResultKey, dataBinder.getBindingResult());
}
}
}
}
那么问题来了,那些类型的name value需要BindingResult哪些不需要呢?
/**
* Whether the given attribute requires a {@link BindingResult} in the model.
*/
private boolean isBindingCandidate(String attributeName, Object value) {
if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return false;
}
Class<?> attrType = (value != null) ? value.getClass() : null;
if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {
return true;
}
return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
}
//集合不行,map不行,简单类型不行!!!
回到ModelAndView getModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//此时已经更新过了model!!!
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
//拿到最新最新的model
ModelMap model = mavContainer.getModel();
//创建最新最新的mav
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
//可以返回了!!!
return mav;
}
返回方法ModelAndView invokeHandleMethod
return getModelAndView(mavContainer, modelFactory, webRequest);
**返回到方法ModelAndView handleInternal
**
返回到方法ModelAndView handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
执行完拦截器的后置方法,开始处理转发结果
渲染视图
将viewName:“forward:/epms” reference to URL “/emps”!
…转发到list方法,过程不再赘述。。。…转发到list方法,过程不再赘述。。
需要注意的是,转发到list的过程中,首先同样执行@ModelAttribute注解的方法getEmployee!!!
list方法的返回值!!!
最终的最终的最终,返回最后的最后的最后的MV!!!
渲染视图
解析视图
- 配置的两个视图解析器
生成View
此时的view 如下图所示:
真正开始渲染视图
。。。。下面的不再赘述!。。。。