SpringMVC

来源:互联网 发布:华为荣耀盒子安装软件 编辑:程序博客网 时间:2024/05/22 03:04

上面讲述的是从 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    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()


【获取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;    }

  • 【通过HandlerExecutionChain获取handler从而得到handlerAdapter!】
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=jik@qq.com, 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=jik@qq.com, 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 如下图所示:

这里写图片描述


真正开始渲染视图

这里写图片描述

。。。。下面的不再赘述!。。。。

0 0
原创粉丝点击