SpringMVC

来源:互联网 发布:java开发工作描述 编辑:程序博客网 时间:2024/05/01 11:19

运行流程示例一将从一个普通的get请求,到返回视图页面结束为一个过程,并使用源码跟踪。

【JSP】:

<a href="emps">List All Employees</a>//很普通一个a标签,没有传参到后台

【Controller】:

@Controllerpublic class EmployeeHandler {    @Autowired    private EmployeeDao employeeDao;    @Autowired    private DepartmentDao departmentDao;    /*先执行这个方法*/    @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  方法执行");    }    /*获取人员列表*/    @RequestMapping("/emps")    public String list(Map<String, Object> map){        map.put("employees", employeeDao.getAll());        System.out.println("list method execute...");        return "list";    }//  @InitBinder//  public void initBinder(WebDataBinder binder){//      binder.setDisallowedFields("lastName");//  }}

正式开始分析:

项目启动,将会加载web.xml , 随之加载springmvc.xml .

【1】启动项目。。。初始化initStrategies

protected void initStrategies(ApplicationContext context) {        initMultipartResolver(context);        initLocaleResolver(context);        initThemeResolver(context);        initHandlerMappings(context);        initHandlerAdapters(context);//        initHandlerExceptionResolvers(context);        initRequestToViewNameTranslator(context);        initViewResolvers(context);//初始化视图解析器        initFlashMapManager(context);    }

看源码注释:

翻译过来就是:初始化servlet所需要使用的策略对象。

/**     * Initialize the strategy objects that this servlet uses.     * <p>May be overridden in subclasses in order to initialize further strategy objects.     */

① 初始化HandlerMappings示例:

/**     * Initialize the HandlerMappings used by this class.     * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,     * we default to BeanNameUrlHandlerMapping.     * 翻译:初始化hadlermapping,这个域名空间中如果没有定义HandlerMapping beans,将会使用默认的BeanNameUrlHandlerMapping     */    private void initHandlerMappings(ApplicationContext context) {        this.handlerMappings = null;        if (this.detectAllHandlerMappings) {            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.            Map<String, HandlerMapping> matchingBeans =                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);            //在这里进行初始化!从上下文中拿属性!            if (!matchingBeans.isEmpty()) {                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());                // We keep HandlerMappings in sorted order.                OrderComparator.sort(this.handlerMappings);                //这里进行了排序            }        }        else {            try {                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);                this.handlerMappings = Collections.singletonList(hm);            }            catch (NoSuchBeanDefinitionException ex) {                // Ignore, we'll add a default HandlerMapping later.            }        }        // Ensure we have at least one HandlerMapping, by registering        // a default HandlerMapping if no other mappings are found.        if (this.handlerMappings == null) {            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);            if (logger.isDebugEnabled()) {                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");            }        }    }
  • 具体在下面语句进行初始化:
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

其他的初始化类似!

【2】点击链接,发出请求!

进入DispatcherServlet的doDispatcher放法:

  • 具体步骤已经在源码中注明!
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {        HttpServletRequest processedRequest = request;        HandlerExecutionChain mappedHandler = null;        boolean multipartRequestParsed = false;        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);        try {            ModelAndView mv = null;            Exception dispatchException = null;            try {                processedRequest = checkMultipart(request);                multipartRequestParsed = processedRequest != request;                // Determine handler for the current request.                mappedHandler = getHandler(processedRequest);                //这里返回一个HandlerExecutionChain!                if (mappedHandler == null || mappedHandler.getHandler() == null) {                    noHandlerFound(processedRequest, response);                    return;                }                // Determine handler adapter for the current request.                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());                //这里返回一个HandlerAdapter,下面就可以开始执行拦截器了!                // Process last-modified header, if supported by the handler.                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)) {                //在这里!执行拦截器额preHandler方法!                    return;                }                try {                    // Actually invoke the handler.                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());                    //调用HandlerAdapter的handler方法真正开始执行目标处理方法。。。最终返回MV!                }                finally {                    if (asyncManager.isConcurrentHandlingStarted()) {                        return;                    }                }                //如果mv没有viewName,就使用默认的this.prefix + transformPath(lookupPath) + this.suffix                applyDefaultViewName(request, mv);                mappedHandler.applyPostHandle(processedRequest, response, mv);            //这里进行拦截器的postHandler方法!            }            catch (Exception ex) {                dispatchException = ex;            }            //处理转发结果!            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);        }        catch (Exception ex) {            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);        }        catch (Error err) {            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);        }        finally {            if (asyncManager.isConcurrentHandlingStarted()) {                // Instead of postHandle and afterCompletion                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);                return;            }            // Clean up any resources used by a multipart request.            if (multipartRequestParsed) {                cleanupMultipart(processedRequest);            }        }    }

【下面开始具体详细分析】:【下面开始具体详细分析】:【下面开始具体详细分析】:【下面开始具体详细分析】


① 获取HandlerExecutionChain:

    • 根据handlerMapping获取HandlerExecutionChain ;
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() + "'");            }            //通过hm获取HEC!            HandlerExecutionChain handler = hm.getHandler(request);            if (handler != null) {                return handler;            }        }        return null;    }
  • 级联方法1 - 获取handler
@Override    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        Object handler = getHandlerInternal(request);        //从请求获取handler        if (handler == null) {            handler = getDefaultHandler();            //如果为空,获取默认的!--springmvc.xml会配置一个默认的servlet--default(为tomcat-web.xml中配置的)        }        if (handler == null) {            return null;        }        // Bean name or resolved handler?        if (handler instanceof String) {            String handlerName = (String) handler;            handler = getApplicationContext().getBean(handlerName);        }        return getHandlerExecutionChain(handler, request);        /*通过handler和request,将拦截器包装进来,见级联方法2*/    }

看第一行获取handler方法:

Object handler = getHandlerInternal(request);

注释如下:

Look up a handler for the given request, returning null if no specific one is found. This method is called by getHandler; a null return value will lead to the default handler, if one is set. //为给定的请求匹配一个handler,如果没有就返回null;//如果返回值为null,使用默认处理器--default handler!!!

源码如下:

类:public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean

方法:

    /**     * Look up a handler method for the given request.     */    @Override    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);        // lookupPath 为请求url,如"/emps"        if (logger.isDebugEnabled()) {            logger.debug("Looking up handler method for path " + lookupPath);        }        //创建 handlerMethod,此时handlerMethod拥有属性bean其值为字符串类Handler(如employeeHandler)         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);        if (logger.isDebugEnabled()) {            if (handlerMethod != null) {                logger.debug("Returning handler method [" + handlerMethod + "]");            }            else {                logger.debug("Did not find handler method for [" + lookupPath + "]");            }        }        //如果handlerMethod 不为空,则创建方法隶属的controller bean handler--即将字符串解析为 object bean        return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;    }

类:org.springframework.web.servlet.handler.
AbstractHandlerMethodMapping<T>.lookupHandlerMethod()

/**     * Look up the best-matching handler method for the current request.     * If multiple matches are found, the best match is selected.     * @param lookupPath mapping lookup path within the current servlet mapping     * @param request the current request     * @return the best-matching handler method, or {@code null} if no match     * @see #handleMatch(Object, String, HttpServletRequest)     * @see #handleNoMatch(Set, String, HttpServletRequest)     */    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {        //此时matches 为空 [] ;        List<Match> matches = new ArrayList<Match>();        //根据请求URL 获取urlMap中与其对应的list对象,如下所示:        /*[{[/emps],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}]*/        List<T> directPathMatches = this.urlMap.get(lookupPath);        if (directPathMatches != null) {            // 添加由match 与handlerMethods.get(mapping)构建的New Match对象于 matches list中            addMatchingMappings(directPathMatches, matches, request);        }        if (matches.isEmpty()) {            // No choice but to go through all mappings            addMatchingMappings(this.handlerMethods.keySet(), matches, request);        }        if (!matches.isEmpty()) {            /*getMappingComparator : Return a comparator for sorting matching mappings.             The returned comparator should sort 'better' matches higher.*/            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));            /*使用构建的comparator 对matches进行排序*/            Collections.sort(matches, comparator);            if (logger.isTraceEnabled()) {                logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);            }            /*获取最好的匹配对象*/            Match bestMatch = matches.get(0);            /*如果matches有多于一个的match,则比较前两个。如果优先级一样,抛出异常*/            if (matches.size() > 1) {                Match secondBestMatch = matches.get(1);                if (comparator.compare(bestMatch, secondBestMatch) == 0) {                    Method m1 = bestMatch.handlerMethod.getMethod();                    Method m2 = secondBestMatch.handlerMethod.getMethod();                    throw new IllegalStateException(                            "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +                            m1 + ", " + m2 + "}");                }            }            handleMatch(bestMatch.mapping, lookupPath, request);            //返回优先级最高的bestMatch的handlerMethod对象。            return bestMatch.handlerMethod;        }        else {            return handleNoMatch(handlerMethods.keySet(), lookupPath, request);        }    }

Match:public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean 的内部类Match如下:

/**     * A thin wrapper around a matched HandlerMethod and its matched mapping for     * the purpose of comparing the best match with a comparator in the context     * of the current request.     */    private class Match {        private final T mapping;        private final HandlerMethod handlerMethod;        private Match(T mapping, HandlerMethod handlerMethod) {            this.mapping = mapping;            this.handlerMethod = handlerMethod;        }        @Override        public String toString() {            return this.mapping.toString();        }    }    private class MatchComparator implements Comparator<Match> {        private final Comparator<T> comparator;        public MatchComparator(Comparator<T> comparator) {            this.comparator = comparator;        }        @Override        public int compare(Match match1, Match match2) {            return this.comparator.compare(match1.mapping, match2.mapping);        }    }

解释如下:

① 一个简单的包装类,包装了handlerMethod与MappingInfo(保存了请求的数据,如参数,方法,映射及headers等);

② 主要用来根据comparator获取最好的Match对象;

③ 返回其handlerMethod


this.urlMap 如下:

  • 保存了url与处理程序方法相关联的映射条件;
  • key为URL,值为对应的映射条件

这里写图片描述


addMatchingMappings执行前:

matches 值:全为null

这里写图片描述

directPathMatches 值:

  • 只有一个由数组组成的对象,size为1,记录了mappingInfo;

这里写图片描述


addMatchingMappings( ) :

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping<T>.addMatchingMappings()

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {        for (T mapping : mappings) {            //获取与request对应的重新构建的新的MappingInfo对象            T match = getMatchingMapping(mapping, request);            if (match != null) {                //根据MappingInfo对象,用对应的handlerMethod构建新的Match对象,添加到 list中                matches.add(new Match(match, handlerMethods.get(mapping)));            }        }    }

注释如下:

Abstract base class for HandlerMapping implementations that define a mapping between a request and a HandlerMethod. //一个抽象基类,其HandlerMapping实现类在请求和处理方法间定义一个映射For each registered handler method, a unique mapping is maintained with subclasses defining the details of the mapping type <T>.//系统中注册的每一个处理方法,都会有一个单独的mapping对象相匹配。

RequestMappingInfoHandlerMapping.getMatchingMapping(RequestMappingInfo info, HttpServletRequest request)

  • 检查给定的RequestMappingInfo 是否匹配当前请求,并根据具体条件返回一个新的RequestMappingInfo 。
/**     * Check if the given RequestMappingInfo matches the current request and     * return a (potentially new) instance with conditions that match the     * current request -- for example with a subset of URL patterns.     * @return an info in case of a match; or {@code null} otherwise.     */    @Override    protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {        return info.getMatchingCondition(request);    }

RequestMappingInfo.getMatchingCondition(HttpServletRequest request)

/**     * Checks if all conditions in this request mapping info match the provided request and returns     * a potentially new request mapping info with conditions tailored to the current request.     * <p>For example the returned instance may contain the subset of URL patterns that match to     * the current request, sorted with best matching patterns on top.     * @return a new instance in case all conditions match; or {@code null} otherwise     */    @Override    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {        RequestMethodsRequestCondition methods = methodsCondition.getMatchingCondition(request);        ParamsRequestCondition params = paramsCondition.getMatchingCondition(request);        HeadersRequestCondition headers = headersCondition.getMatchingCondition(request);        ConsumesRequestCondition consumes = consumesCondition.getMatchingCondition(request);        ProducesRequestCondition produces = producesCondition.getMatchingCondition(request);        if (methods == null || params == null || headers == null || consumes == null || produces == null) {            return null;        }        PatternsRequestCondition patterns = patternsCondition.getMatchingCondition(request);        if (patterns == null) {            return null;        }        RequestConditionHolder custom = customConditionHolder.getMatchingCondition(request);        if (custom == null) {            return null;        }        //返回新的RequestMappingInfo对象        return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition());    }

addMatchingMappings执行后:

matches 值:

这里写图片描述


RequestMappingInfoHandlerMapping.handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request)

@Override    protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {        super.handleMatch(info, lookupPath, request);        String bestPattern;        Map<String, String> uriVariables;        Map<String, String> decodedUriVariables;        Set<String> patterns = info.getPatternsCondition().getPatterns();        if (patterns.isEmpty()) {            bestPattern = lookupPath;            uriVariables = Collections.emptyMap();            decodedUriVariables = Collections.emptyMap();        }        else {            bestPattern = patterns.iterator().next();            uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);            decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);        }    /*往请求域中设置属性*/   request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);        if (isMatrixVariableContentAvailable()) {            Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);            request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);        }        if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {            Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();            request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);        }    }

注释如下:

Expose URI template variables, matrix variables, and producible media types in the request.

super.handleMatch:

protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {        request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);    }

这里写图片描述


return bestMatch.handlerMethod :

这里写图片描述


以上都在lookupHandlerMethod(lookupPath,request)方法中!!


创建后handlerMethod 的值:

这里写图片描述


执行HandlerMethod getHandlerInternal()最后返回结果:

return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;

handlerMethod.createWithResolvedBean():

    /**     * If the provided instance contains a bean name rather than an object instance, the bean name is resolved     * before a {@link HandlerMethod} is created and returned.     */    public HandlerMethod createWithResolvedBean() {        Object handler = this.bean;        if (this.bean instanceof String) {            String beanName = (String) this.bean;            handler = this.beanFactory.getBean(beanName);        }        //返回新的HandlerMethod对象        return new HandlerMethod(this, handler);    }

获取的handler:

这里写图片描述


返回的新的HandlerMethod对象:

    /**     * Re-create HandlerMethod with the resolved handler.     */    private HandlerMethod(HandlerMethod handlerMethod, Object handler) {        Assert.notNull(handlerMethod, "handlerMethod is required");        Assert.notNull(handler, "handler is required");        this.bean = handler;        this.beanFactory = handlerMethod.beanFactory;        this.method = handlerMethod.method;        this.bridgedMethod = handlerMethod.bridgedMethod;        this.parameters = handlerMethod.parameters;    }

最终获取的handler:

    • 此时属性bean 不再是字符串,而是object!

这里写图片描述


为添加拦截器作准备!!!


  • 级联方法2–添加interceptor!
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {        HandlerExecutionChain chain =            (handler instanceof HandlerExecutionChain) ?                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);        chain.addInterceptors(getAdaptedInterceptors());        //初始化拦截器列表,并为添加到chain中!!!        String lookupPath = urlPathHelper.getLookupPathForRequest(request);        for (MappedInterceptor mappedInterceptor : mappedInterceptors) {            //根据映射URL,添加匹配的拦截器            if (mappedInterceptor.matches(lookupPath, pathMatcher)) {                chain.addInterceptor(mappedInterceptor.getInterceptor());                //添加匹配的拦截器于chain中!!!            }        }        return chain;    }
  • HanlerMapping 有四种:

这里写图片描述

这里写图片描述

至此,拿到了hanler和拦截器链!


② 获取HandlerAdapter:

    • 根据handlerExecutionChain返回的handler得到HandlerAdapter
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");    }

如下图所示,springmvc里面使用了<mvc:annotaion-driven/> ,会有三个默认的adapter!

这里写图片描述

这里使用的是第三种:RequestMappingHandlerAdapter!


③ 开始执行拦截器的preHandler方法!

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {        if (getInterceptors() != null) {            for (int i = 0; i < getInterceptors().length; i++) {                HandlerInterceptor interceptor = getInterceptors()[i];                if (!interceptor.preHandle(request, response, this.handler)) {                    triggerAfterCompletion(request, response, null);                    return false;                }                this.interceptorIndex = i;            }        }        return true;        //执行成功,返回true!    }
  • 第一个拦截器

这里写图片描述

拦截的是 DefaultFormattingConversionService,里面涵盖了众多数据格式转换,其值如下!

ConversionService converters =     @org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1d0bfedd,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    @org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1d0bfedd    @org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1d0bfedd    @org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    @org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    @org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    @org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    @org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    @org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@71933d6c    java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@26d6466f    java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@4d29efd0    java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@6ebfc8d0    java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@13f17c9e    java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@5f0e0edb    java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@7c3f6843    java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@32df804e    java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2f5e8a8f    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1d0bfedd,java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1d0bfedd    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1d0bfedd    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@36536500    java.lang.String -> com.atguigu.springmvc.crud.entities.Employee : com.atguigu.springmvc.converters.EmployeeConverter@7cd76237    java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@3daa3554    java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@67daca94    java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@79a5b7b4    java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@6f609af9    java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@7119087d    java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@1bdbbaab    java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@40c19080    java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@257db177    java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@545532da    java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@7e896e10    java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@70991d56    java.util.Locale -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@227c808d    java.util.Properties -> java.lang.String : org.springframework.core.convert.support.PropertiesToStringConverter@76bc3ed6    java.util.UUID -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@63f5e4b6    org.springframework.core.convert.support.ArrayToArrayConverter@7abb0257    org.springframework.core.convert.support.ArrayToCollectionConverter@247a9bff    org.springframework.core.convert.support.ArrayToObjectConverter@51ad65f7    org.springframework.core.convert.support.ArrayToStringConverter@5704ce99    org.springframework.core.convert.support.ByteBufferConverter@71f84d01    org.springframework.core.convert.support.ByteBufferConverter@71f84d01    org.springframework.core.convert.support.CollectionToArrayConverter@6d97950e    org.springframework.core.convert.support.CollectionToCollectionConverter@b96dcb4    org.springframework.core.convert.support.CollectionToObjectConverter@3a5367af    org.springframework.core.convert.support.CollectionToStringConverter@648f53c8    org.springframework.core.convert.support.FallbackObjectToStringConverter@310ab67f    org.springframework.core.convert.support.IdToEntityConverter@6487b71b,org.springframework.core.convert.support.ObjectToObjectConverter@a06812d    org.springframework.core.convert.support.MapToMapConverter@5d73b35d    org.springframework.core.convert.support.ObjectToArrayConverter@4f016505    org.springframework.core.convert.support.ObjectToCollectionConverter@39bc3013    org.springframework.core.convert.support.StringToArrayConverter@7c97cb70    org.springframework.core.convert.support.StringToCollectionConverter@7dc5a30b
  • 第二个拦截器

自定义拦截器

这里写图片描述

    • 执行其preHandler方法:

这里写图片描述

  • 第三个拦截器

i18n拦截器

这里写图片描述

至此,拦截器的preHandler方法执行完毕!!

开始执行真正的目标方法!


———————-RequestMappingHandlerAdapter.handle()方法!————————————-

④ ha.handle–超级重要的一个方法

主要有这样几个功能:

① 对数据进行格式化转换和校验;
② 尝试调用@ModelAttribute注解的方法;
③ 为你的目标方法参数赋值;
④ 执行你的目标方法;
⑤ 拿到你方法的返回值。。最终拿到一个MV

// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这里写图片描述

点击可以看到,其有五类实现;上面已经说明,实际获取的是RequestMappingHandlerAdapter !

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,        InitializingBean {        //...}

其中mappedHandler.getHandler( ):

这里写图片描述


  • 级联方法3-ModelAndView handle()
/**     * {@inheritDoc} <p>This implementation expects the handler to be an {@link HandlerMethod}.     */    @Override    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return handleInternal(request, response, (HandlerMethod) handler);        //调用handleInternal 方法--见级联方法4    }
  • 级联方法4-ModelAndView handleInternal()

这个方法名字很有意思,“处理内部的”!

    @Override    protected final ModelAndView handleInternal(HttpServletRequest request,            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        //判断有没有sessionAttributes        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);        //执行目标方法--见级联方法5    }

  • 级联方法5-ModelAndView invokeHandleMethod()

——我要开始执行目标方法啦!—-最后还要回到这个方法!!!

private ModelAndView invokeHandleMethod(HttpServletRequest request,            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ServletWebRequest webRequest = new ServletWebRequest(request, response);        /*获取数据绑定工厂*/        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);        /*获取model工厂*/        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);        /*为请求映射的方法配置属性策略,--请看级联方法6*/        ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);        /* 创建一个新的mav容器实例 */        ModelAndViewContainer mavContainer = new ModelAndViewContainer();        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));           /*请求域中的map对象放到容器中*/        modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);        /*初始化model-请看级联方法7--这个方法很重要!!!注解@ModelAttribute方法就是在这里被调用!!!*/        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);        }        /*请看级联方法3-3 这里的invokeAndHandle为ServletInvocableHandlerMethod.invokeAndHandle*/        requestMappingMethod.invokeAndHandle(webRequest, mavContainer);        if (asyncManager.isConcurrentHandlingStarted()) {            return null;        }        /* 这里这里!!! 拿到最新的MV!!! */        return getModelAndView(mavContainer, modelFactory, webRequest);    }

    • 级联方法6 ServletInvocableHandlerMethod createRequestMappingMethod()
private ServletInvocableHandlerMethod createRequestMappingMethod(            HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {        ServletInvocableHandlerMethod requestMethod;        requestMethod = new ServletInvocableHandlerMethod(handlerMethod);        requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);        //设置方法参数解析器        requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);        //设置方法返回值处理器        requestMethod.setDataBinderFactory(binderFactory);        //设置数据绑定工厂        requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);        //设置参数名发现者        return requestMethod;    }
    • 级联方法7void initModel

————上面说了,你的@ModelAttribute方法就是在这里被调用!!!

/**     * Populate the model in the following order:     * <ol>     *  <li>Retrieve "known" session attributes -- i.e. attributes listed via     *  {@link SessionAttributes @SessionAttributes} and previously stored in     *  the in the model at least once     * //尝试从session中获取attribute     *  <li>Invoke {@link ModelAttribute @ModelAttribute} methods     * //调用该使用@ModelAttribute的方法!     *  <li>Find method arguments eligible as session attributes and retrieve     *  them if they're not already present in the model     * //如果model没有方法参数对应的model,就从session里面获取     * </ol>     * @param request the current request     * @param mavContainer contains the model to be initialized     * @param handlerMethod the method for which the model is initialized     * @throws Exception may arise from {@code @ModelAttribute} methods     */    public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)            throws Exception {//从session里面获取Map<String, ?> attributesInSession/*Retrieve "known" attributes from the session, i.e. attributes listed by name in @SessionAttributes or attributes previously stored in the model that matched by type.*/        Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);        mavContainer.mergeAttributes(attributesInSession);        //放入modelmap        //见级联方法8        invokeModelAttributeMethods(request, mavContainer);        //!!!注意 ,执行ModelAttributeMethods(即,有@ModelAttribute注解的方法)        for (String name : findSessionAttributeArguments(handlerMethod)) {            if (!mavContainer.containsAttribute(name)) {                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);                if (value == null) {                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");                }                mavContainer.addAttribute(name, value);            }        }    }
      • 级联方法8void invokeModelAttributeMethods()

【! 真正开始执行 @ModelAttribute注解的方法!】

/**     * Invoke model attribute methods to populate the model. Attributes are     * added only if not already present in the model.     */    private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)            throws Exception {        for (InvocableHandlerMethod attrMethod : this.attributeMethods) {            //获取注解的value值!!!            String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();            if (mavContainer.containsAttribute(modelName)) {                continue;            }            //在给定请求的上下文中解析参数值后调用该方法。/* 这里InvocableHandlerMethod.invokeForRequest;这里这个返回值只是为了下面的if方法进行判断,并不会当成视图!!!只会对MV有作用--见级联方法9*/            Object returnValue = attrMethod.invokeForRequest(request, mavContainer);            /*Returns true if the method return type is void, false otherwise.如果返回类型不为空,将执行下面的方法;即可以返回一个对象,那么SpringMVC将会根据对象类型,对象值,放modelmap中一个key:value*/            if (!attrMethod.isVoid()){                String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());                if (!mavContainer.containsAttribute(returnValueName)) {                    mavContainer.addAttribute(returnValueName, returnValue);                }            }        }    }

可以看到方法名,参数个数及类型等信息!

这里写图片描述

        • 级联方法9Object invokeForRequest

—-注意,现在是为@ModelAttribute方法从请求里面解析参数值并赋于方法对应参数!!!

/**     * Invoke the method after resolving its argument values in the context of the given request. <p>Argument     * values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}     * parameter however may supply argument values to be used directly, i.e. without argument resolution.     * Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or     * a thrown exception instance. Provided argument values are checked before argument resolvers.     * @param request the current request     * @param mavContainer the ModelAndViewContainer for this request     * @param providedArgs "given" arguments matched by type, not resolved     * @return the raw value returned by the invoked method     * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception     */    public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,            Object... providedArgs) throws Exception {        //获取方法参数值(使用request域中属性对象为方法参数赋值)--见级联方法10        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;    }
          • 级联方法10Object[] getMethodArgumentValues

这里返回一个object数组

方法参数值数组:解析从request中获取的值,为对应的方法参数赋值,然后返回!

/**     * Get the method argument values for the current request.     * 从当前请求中获取方法参数值     */    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,            Object... providedArgs) throws Exception {        MethodParameter[] parameters = getMethodParameters();        Object[] args = new Object[parameters.length];        for (int i = 0; i < parameters.length; i++) {            MethodParameter parameter = parameters[i];            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);            GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());            //试图从提供的参数值列表中解析方法参数。            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;    }

这里说明一下参数解析过程:

①Object org.springframework.web.method.support.InvocableHandlerMethod.resolveProvidedArgument(){/*试图从提供的参数值列表中解析方法参数。*/            args[i] = resolveProvidedArgument(parameter, providedArgs);}/*如果上面获取不到值,则从下面获取*/① Object org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(){args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);/*返回到Object org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest*/}③Object org.springframework.web.method.support.HandlerMethodArgumentResolver.resolveArgument(){return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);//返回到②}/*【 此时resolver 为RequestParamMethodArgumentResolver;这里针对不同方法参数类型可能有所不同!!!】*/以下为下面这个方法的注释:/*Resolves a method parameter into an argument value from a given request. --将方法参数从给定的请求转换为参数值。A ModelAndViewContainer provides access to the model for the request.-- 针对该请求,MV容器提供了一个通道使其可以使用model对象。 A WebDataBinderFactory provides a way to create a WebDataBinder instance when needed for data binding and type conversion purposes. --当需要数据绑定或者类型转换、格式化时,WebDataBinder工厂提供了一个通道去创建一个WebDataBinder instance*/【也就是说,你的数据绑定和类型转换,是在这里!!!】④ 调用父类的org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(){    /*拿到参数类型*/    Class<?> paramType = parameter.getParameterType();    /*拿到参数值信息*/        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);/*Resolves the given parameter type and value name into an argument value.意思是根据给定的参数类型和注解使用的value name,从request中获取对应的值,赋予方法参数!!!*/        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) {            /*根据请求对象和 id1(方法参数的key) 创建binder对象--见数据绑定-1*/            WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);            /*Convert the value to the required type (if necessary from a String). 根据参数类型对arg进行数据格式转换*/            arg = binder.convertIfNecessary(arg, paramType, parameter);        }        /*Invoked after a value is resolved.在值解析后被调用--解析完id1了,arg为null,但是也要返回啊*/        handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);        return arg;        /*返回到③*/}

拿到的参数值信息如下所示,默认值为null,name属性为id1(@RequestParam(value=”id1”,required=false)):

这里写图片描述


数据流程绑定-1

/**     * Create a new {@link WebDataBinder} for the given target object and     * initialize it through a {@link WebBindingInitializer}.     * @throws Exception in case of invalid state or arguments     */    @Override    public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)            throws Exception {            /*创建dataBinder实例,target此时为null,objectName为 id1 ;            其中createBinderInstance = return new ExtendedServletRequestDataBinder(target, objectName);            */        WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);        if (this.initializer != null) {            //初始化Binder --见数据绑定流程-2            this.initializer.initBinder(dataBinder, webRequest);        }        //进一步初始化创建的数据:Extension point to further initialize the created data binder instance (e.g. with @InitBinder methods) after "global" initializaton via WebBindingInitializer.        initBinder(dataBinder, webRequest);        return dataBinder;    }

数据绑定流程-2

@Override    public void initBinder(WebDataBinder binder, WebRequest request) {        binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);        if (this.directFieldAccess) {            binder.initDirectFieldAccess();        }        if (this.messageCodesResolver != null) {            binder.setMessageCodesResolver(this.messageCodesResolver);        }        if (this.bindingErrorProcessor != null) {            binder.setBindingErrorProcessor(this.bindingErrorProcessor);        }        //validator!!!数据校验        if (this.validator != null && binder.getTarget() != null &&                this.validator.supports(binder.getTarget().getClass())) {            binder.setValidator(this.validator);        }        //conversionService !!!数据转换和格式化        if (this.conversionService != null) {            binder.setConversionService(this.conversionService);        }        if (this.propertyEditorRegistrars != null) {            for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {                propertyEditorRegistrar.registerCustomEditors(binder);            }        }    }

此时的dataBinder如下:

这里写图片描述


这时回到Object resolveArgument( )方法,即参数解析的最后一个过程:

@Override    public final Object resolveArgument(            MethodParameter parameter, ModelAndViewContainer mavContainer,            NativeWebRequest webRequest, WebDataBinderFactory binderFactory)            throws Exception {        Class<?> paramType = parameter.getParameterType();        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);        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对象*/            WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);            /* 尝试进行数据格式转换 */            arg = binder.convertIfNecessary(arg, paramType, parameter);        }        /*Invoked after a value is resolved*/        handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);        return arg;        //返回解析到的参数值    }

现在回到 级联方法9Object invokeForRequest!!

此时的args如下图所示:

这里写图片描述


getEmployee方法的参数值解析完毕:id 为null,map为默认值 BindingAwareModelMap!


开始执行@ModelAttribute注解的方法(getEmployee)

Object returnValue = invoke(args);

如下图所示,现在才开始执行该方法,参数值已经解析过了!!

这里写图片描述

再次回到级联方法9Object invokeForRequest!!

  • 此时方法的返回值为null(getEmployee方法没有返回值)

  • Object returnValue = invoke(args);//该句调用真正的@ModelAttribute注解的方法,开始执行你的getEmployee!!!

  • 这里返回值为null

这里写图片描述


回到级联方法8void invokeModelAttributeMethods()

  • returnValue = null:
  • 这里的returnValue 只是为了下面的 if 方法判断使用,不会返回!!!

这里写图片描述


回到级联方法7void initModel

public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)            throws Exception {        Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);        mavContainer.mergeAttributes(attributesInSession);        invokeModelAttributeMethods(request, mavContainer);/*开始继续从这里执行:如果session中有@ModelAttribute参数对应的key,那么从session中获取该value,放到modelmap中(进而放到mav容器中);如果有key,但是没有value,则会抛出异常!!!*/        for (String name : findSessionAttributeArguments(handlerMethod)) {            if (!mavContainer.containsAttribute(name)) {                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);                if (value == null) {                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");                }                mavContainer.addAttribute(name, value);            }        }    }

回到级联方法3-ModelAndView invokeHandleMethod()!!!

/**     * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} if view resolution is required.     */    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));        //终于从这里出来了,开始执行下面的方法。@ModelAttribute方法已经调用,MV已经有了初步值!!!        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);        }        //Invokes the method and handles the return value through a registered HandlerMethodReturnValueHandler.        //*调用的方法和处理返回值通过注册的handlermethodreturnvaluehandler。invokeAndHandle将会调用父类的invokeForRequest请看级联方法10*/        requestMappingMethod.invokeAndHandle(webRequest, mavContainer);        if (asyncManager.isConcurrentHandlingStarted()) {            return null;        }        return getModelAndView(mavContainer, modelFactory, webRequest);    }


再一次重复为目标方法参数赋值的过程!但这时方法已经成为了list,就是真正匹配你的requestMapping那个方法!!!

【执行调用的目标方法时,总要事先为目标方法的参数赋值。@ModelAttribute注解的方法如此,普通的目标方法也如此!】

具体过程如下:

【开始调用你的目标方法】

此时class : .ServletInvocableHandlerMethod

看注释:Invokes the method and handles the return value through a registered
–意思是调用这个方法,并通过注册的handler 处理返回值!!!


级联方法10 void invokeAndHandle

public final void invokeAndHandle(ServletWebRequest webRequest,            ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {        //Invoke the method after resolving its argument values in the context of the given request.         //将会调用父类的该方法(InvocableHandlerMethod)        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;        }    }

……..go on!

继续解析目标方法的参数过程!

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() + "]");        //注意这里 resolver!!!已经变成了MapMethodProcessor        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);    }

这里写图片描述

………..继续解析参数!!!

这里写图片描述


。。。。继续。。。直到调用list方法!

此时,args改变!

这里写图片描述

returnValue 改变!

这里写图片描述

看到没? returnValue 不再是null 而是你方法返回的视图名! list!!


处理返回值–再次回到级联方法10void invokeAndHandle

public final void invokeAndHandle(ServletWebRequest webRequest,            ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {        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 {        //这里这里这里!!!返回值不为空开始处理返回值!!!见下属级联方法11            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;        }    }

可以看到,有12个处理返回值的handler,这里使用的是org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite@1f5a0bc0

这里写图片描述
这里写图片描述

级联方法11

/**     * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.     * @exception IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.     */    @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() + "]");        // Handle the given return value by adding attributes to the model and setting a view or setting the ModelAndViewContainer.setRequestHandled flag to true to indicate the response has been handled directly.        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);        //见下属级联方法12    }

此时的handler为:ViewNameMethodReturnValueHandler

这里写图片描述

级联方法12–真正处理返回值的方法:

@Override    public void handleReturnValue(            Object returnValue, MethodParameter returnType,            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)            throws Exception {        if (returnValue == null) {            return;        }        else if (returnValue instanceof String) {            //这里!!!将返回值作为视图名!            String viewName = (String) returnValue;            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());        }    }

mavContainer也已经有了employee list

(因为在list方法里面手动放入了啊,这也说明另外一个问题,无论你方法中使用的是map还是model还是mv,那么最终都会对应一个对象,BindingAwareModelMap!!!)

这里写图片描述


处理完方法返回值了,再次回到级联方法3-ModelAndView RequestMappingHandlerAdapter.invokeHandleMethod()

千辛万苦拿到MV

这里写图片描述

此时的MV已经包含初步的视图名和model

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {        /* 注意这里 更新model 见下属级联方法13*/        modelFactory.updateModel(webRequest, mavContainer);        if (mavContainer.isRequestHandled()) {            return null;        }        ModelMap model = mavContainer.getModel();        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;    }

这里写图片描述

级联方法13

public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {        if (mavContainer.getSessionStatus().isComplete()){            this.sessionAttributesHandler.cleanupAttributes(request);        }        else {            this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());        }        /*Whether the request is handled directly.*/        if (!mavContainer.isRequestHandled()) {            updateBindingResult(request, mavContainer.getModel());            /*Add BindingResult attributes to the model for attributes that require it. 见下属级联方法14*/        }    }

级联方法14

     */    private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {        List<String> keyNames = new ArrayList<String>(model.keySet());        for (String name : keyNames) {            Object value = model.get(name);            if (isBindingCandidate(name, value)) {                String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;                if (!model.containsAttribute(bindingResultKey)) {                    WebDataBinder dataBinder = binderFactory.createBinder(request, value, name);                    //看这里看这里!!!从dataBingder拿属性放到model里面!!!                    model.put(bindingResultKey, dataBinder.getBindingResult());                }            }        }    }

返回到级联方法2-ModelAndView handleInternal()!!!

@Override    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);                }            }        }        //Invoke the RequestMapping handler method preparing a ModelAndView if view resolution is required        return invokeHandleMethod(request, response, handlerMethod);    }

ModelAndView 已经彻底准备好了!!!


回到级联方法1-ModelAndView handle()

这里写图片描述

至此,HandlerAdapter的handle方法处理完毕!!!



回到DispatcherServlet的doDispatcher方法!

⑤ 执行拦截器的postHandle方法!!

mappedHandler.applyPostHandle(processedRequest, response, mv);
/**     * Apply postHandle methods of registered interceptors.     */    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {        if (getInterceptors() == null) {            return;        }        //拦截器执行顺序颠倒!!!        for (int i = getInterceptors().length - 1; i >= 0; i--) {            HandlerInterceptor interceptor = getInterceptors()[i];            //执行postHanle方法!!            interceptor.postHandle(request, response, this.handler, mv);        }    }

这里写图片描述

⑥ 执行处理转发结果

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {        boolean errorView = false;        if (exception != null) {            if (exception instanceof ModelAndViewDefiningException) {                logger.debug("ModelAndViewDefiningException encountered", exception);                mv = ((ModelAndViewDefiningException) exception).getModelAndView();            }            else {                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);                mv = processHandlerException(request, response, handler, exception);                errorView = (mv != null);            }        }        // Did the handler return a view to render?        if (mv != null && !mv.wasCleared()) {            //视图渲染!!!Render the given ModelAndView.请往下看级联方法15        /* This is the last stage in handling a request. It may involve resolving the view by name.*/            render(mv, request, response);            if (errorView) {                WebUtils.clearErrorRequestAttributes(request);            }        }        else {            if (logger.isDebugEnabled()) {                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +                        "': assuming HandlerAdapter completed request handling");            }        }        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {            // Concurrent handling started during a forward            return;        }        if (mappedHandler != null) {            mappedHandler.triggerAfterCompletion(request, response, null);        }    }

级联方法15-void render

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {        // Determine locale for request and apply it to the response.        Locale locale = this.localeResolver.resolveLocale(request);        response.setLocale(locale);        View view;        if (mv.isReference()) {            // We need to resolve the view name.            //通过视图解析器,将逻辑视图转换为物理视图            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);            if (view == null) {                throw new ServletException(                        "Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" +                                getServletName() + "'");            }        }        else {            // No need to lookup: the ModelAndView object contains the actual View object.            view = mv.getView();            if (view == null) {                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +                        "View object in servlet with name '" + getServletName() + "'");            }        }        // Delegate to the View object for rendering.        if (logger.isDebugEnabled()) {            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");        }        try {            //在这里进行视图渲染            view.render(mv.getModelInternal(), request, response);        }        catch (Exception ex) {            if (logger.isDebugEnabled()) {                logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '"                        + getServletName() + "'", ex);            }            throw ex;        }    }

看,JstlView,name为list,URL!!

这里写图片描述

进行真正的视图渲染!

view.render(mv.getModelInternal(), request, response);

注释如下:

Render the view given the specified model. --使用给定的model渲染视图The first step will be preparing the request: In the JSP case, this would mean setting model objects as request attributes.--第一步将model objects 设置为 request attributes---所以你的model默认是在request中!!! The second step will be the actual rendering of the view, for example including the JSP via a RequestDispatcher. --第二部才是真正的视图渲染,例如 请求转发到一个JSP!

还没完!

还要执行拦截器的afterCompletion!

mappedHandler.triggerAfterCompletion(request, response, null);

这里写图片描述

具体方法如下:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)            throws Exception {        if (getInterceptors() == null) {            return;        }        for (int i = this.interceptorIndex; i >= 0; i--) {            HandlerInterceptor interceptor = getInterceptors()[i];            try {                interceptor.afterCompletion(request, response, this.handler, ex);            }            catch (Throwable ex2) {                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);            }        }    }

执行次序为从后到前:

这里写图片描述

好了,结束了,接下来是DispatcherServlet.doService()!

这里写图片描述

0 0