springmvc--4--HandlerAdapter处理器适配器

springmvc–HandlerAdapter处理器适配器

文章目录

1 用途

帮助DispatcherServlet调用处理器方法。

DispatcherServlet不管实际如何调用该处理器方法,它只需要调用handlerAdapter.handle(processedRequest, response, mappedHandler.getHandler())方法,即完成了处理器方法调用。具体的调用逻辑由HandlerAdapter实现类完成。

2 springmvc默认的HandlerAdapter

springmvc会自动的注册4种类型的HandlerAdapter,它们之间的关系如下面类图所示

在这里插入图片描述

注意:HandlerMappingHandlerAdapter是一一对应的,而上篇文章说的是RequestMappingHandlerMapping,所以这里只看RequestMappingHandlerAdapter(支持@RequestMapping注解)。况且另外3种方式基本上被淘汰了。

3 HandlerAdapter接口

public interface HandlerAdapter {

    /**
     * 该处理器适配器是否支持该处理器方法
     */
    boolean supports(Object handler);

    /**
     * 完成该处理器方法的调用
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * Same contract as for HttpServlet's {@code getLastModified} method.
     * Can simply return -1 if there's no support in the handler class.
     * @param request current HTTP request
     * @param handler the handler to use
     * @return the lastModified value for the given handler
     * @see javax.servlet.http.HttpServlet#getLastModified
     * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
     */
    long getLastModified(HttpServletRequest request, Object handler);

}

该接口中有3个方法,下面我们就来看一下这3个方法的具体原理

4 RequestMappingHandlerAdapter的构造方法

//消息转换器
private List<HttpMessageConverter<?>> messageConverters;

public RequestMappingHandlerAdapter() {
   this.messageConverters = new ArrayList<>(4);
   this.messageConverters.add(new ByteArrayHttpMessageConverter());
   this.messageConverters.add(new StringHttpMessageConverter());
   try {
      this.messageConverters.add(new SourceHttpMessageConverter<>());
   }
   catch (Error err) {
      // Ignore when no TransformerFactory implementation is available
   }
   this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

构造方法只是向里面放入几个HttpMessageConverter

  • ByteArrayHttpMessageConverter
  • StringHttpMessageConverter
  • SourceHttpMessageConverter
  • AllEncompassingFormHttpMessageConverter

5 afterPropertiesSet()方法(InitializingBean接口唯一方法)

该方法在bean初始化阶段被调用

/**
 * 这3个属性类型是典型的组合模式。
 * XXXResolverComposite持有多个解析器,内部通过遍历这些解析器,使用第一个可以完成解析的解析器
 * 进行解析
 */
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;


@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    //找出所有标注@ControllerAdvice注解的bean,并进行初步处理,见5.1
    initControllerAdviceCache();

    //用户未进行手动配置,就会使用springmvc默认配置的解析器,见5.2
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

5.1 找出所有标注@ControllerAdvice注解的bean,并进行初步处理

//过滤器,有@ModelAttribute注解没有@RequestMapping注解的方法
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
    (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
     AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
//过滤器,有@InitBinder注解的方法
public static final MethodFilter INIT_BINDER_METHODS = method ->
    AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);

//保存RequestBodyAdvice对象和ResponseBodyAdvice
private final List<Object> requestResponseBodyAdvice = new ArrayList<>();


private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    //获取容器中有@ControllerAdvice注解的bean,并封装为ControllerAdviceBean,见9
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

    //下面开始对@ControllerAdvice注解的bean进行处理
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        /**
         * 会对@ControllerAdvice注解标注的bean过滤
         * 得到有@ModelAttribute注解没有@RequestMapping注解的方法
         */
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        //缓存
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        /**
         * 会对@ControllerAdvice注解标注的bean过滤
         * 得到有@InitBinder注解的方法
         */
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        //缓存
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        /**
         * RequestBodyAdvice允许用户在读取和类型转换之前自定义请求体
         * ResponseBodyAdvice允许用户@ResponseBody方法之后,HttpMessageConverter写入正文之前
         * 自定义响应体
         */
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }

    /**
     * 使用<mvc:annotation-driven>,会自动向requestResponseBodyAdvice属性中填充两个值
     * JsonViewRequestBodyAdvice和JsonViewResponseBodyAdvice
     */
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }

    if (logger.isDebugEnabled()) {
        int modelSize = this.modelAttributeAdviceCache.size();
        int binderSize = this.initBinderAdviceCache.size();
        int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
        int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
        if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
            logger.debug("ControllerAdvice beans: none");
        }
        else {
            logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
                         " @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
        }
    }
}

总结一下该方法做的事情

  • 找到容器中有@ControllerAdvice注解的bean,并封装为ControllerAdviceBean
  • 对这些bean中的方法分类
    • @ModelAttribute注解没有@RequestMapping注解的方法,保存到modelAttributeAdviceCache属性中
    • @InitBinder注解的方法,保存到initBinderAdviceCache属性中
    • 实现了RequestBodyAdvice接口或ResponseBodyAdvice接口的方法,保存到requestResponseBodyAdvice属性中

注意:springmvc配置文件中使用<mvc:annotation-driven>,会自动向requestResponseBodyAdvice属性中填充两个值

  • JsonViewRequestBodyAdvice
  • JsonViewResponseBodyAdvice

5.2 springmvc三种解析器(参数,绑定器,返回值)的默认配置

/**
 * Return the list of argument resolvers to use including built-in resolvers
 * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
 */
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}


/**
 * Return the list of argument resolvers to use for {@code @InitBinder}
 * methods including built-in and custom resolvers.
 */
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

    return resolvers;
}


/**
 * Return the list of return value handlers to use including built-in and
 * custom handlers provided via {@link #setReturnValueHandlers}.
 */
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

    // Single-purpose return value types
    handlers.add(new ModelAndViewMethodReturnValueHandler());
    handlers.add(new ModelMethodProcessor());
    handlers.add(new ViewMethodReturnValueHandler());
    handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
                                                           this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
    handlers.add(new StreamingResponseBodyReturnValueHandler());
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
                                               this.contentNegotiationManager, this.requestResponseBodyAdvice));
    handlers.add(new HttpHeadersReturnValueHandler());
    handlers.add(new CallableMethodReturnValueHandler());
    handlers.add(new DeferredResultMethodReturnValueHandler());
    handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

    // Annotation-based return value types
    handlers.add(new ModelAttributeMethodProcessor(false));
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
                                                        this.contentNegotiationManager, this.requestResponseBodyAdvice));

    // Multi-purpose return value types
    handlers.add(new ViewNameMethodReturnValueHandler());
    handlers.add(new MapMethodProcessor());

    // Custom return value types
    if (getCustomReturnValueHandlers() != null) {
        handlers.addAll(getCustomReturnValueHandlers());
    }

    // Catch-all
    if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
        handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    }
    else {
        handlers.add(new ModelAttributeMethodProcessor(true));
    }

    return handlers;
}

这么多的解析器,不需要知道原理,看看名字,知道它对应的解析类型就行了。

6 supports(Object handler)方法,判断处理器适配器是否支持该处理器方法

该方法在AbstractHandlerMethodAdapter中实现

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 * @param handler the handler instance to check
 * @return whether or not this adapter can adapt the given handler
 */
@Override
public final boolean supports(Object handler) {
    //只要这两个条件成立,就表明支持该处理器方法
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

RequestMappingHandlerAdapter中实现supportsInternal()方法

/**
 * Always return {@code true} since any method argument and return value
 * type will be processed in some way. A method argument not recognized
 * by any HandlerMethodArgumentResolver is interpreted as a request parameter
 * if it is a simple type, or as a model attribute otherwise. A return value
 * not recognized by any HandlerMethodReturnValueHandler will be interpreted
 * as a model attribute.
 */
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
   return true;
}

从上面代码我们可以发现,只要处理器方法是HandlerMethod类型的,就支持

7 getLastModified(HttpServletRequest request, Object handler)方法,获取上一次修改请求的时间

该方法在AbstractHandlerMethodAdapter中实现

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
    return getLastModifiedInternal(request, (HandlerMethod) handler);
}

RequestMappingHandlerAdapter中实现getLastModifiedInternal()方法

/**
 * This implementation always returns -1. An {@code @RequestMapping} method can
 * calculate the lastModified value, call {@link WebRequest#checkNotModified(long)},
 * and return {@code null} if the result of that call is {@code true}.
 */
@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
   return -1;
}

总是返回-1,不支持这个功能

8 handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法, 完成处理器方法的调用

该方法在AbstractHandlerMethodAdapter中实现

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
}

RequestMappingHandlerAdapter中实现handleInternal()方法,真正的方法调用逻辑

//一个响应头属性
protected static final String HEADER_CACHE_CONTROL = "Cache-Control";

//缓存的秒数,0表示不缓存
private int cacheSecondsForSessionAttributeHandlers = 0;

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
                                      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    //检查请求,见8.1
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    /**
     * 会话同步
     * synchronizeOnSession默认是false
     */
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    //非同步
    else {
        // No synchronization on session demanded at all...
        //执行处理器方法,返回一个ModelAndView对象,见8.2
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    //不包含此响应头
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        //用户使用@SessionAttributes注解指定了属性
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            //为资源设置缓存时间
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}

8.1 checkRequest(HttpServletRequest request)方法,检查请求

//支持的请求方式,默认为null,表示支持所有
@Nullable
private Set<String> supportedMethods;
//session是否必须
private boolean requireSession = false;

/**
 * Check the given request for supported methods and a required session, if any.
 * @param request current HTTP request
 * @throws ServletException if the request cannot be handled because a check failed
 * @since 4.2
 */
protected final void checkRequest(HttpServletRequest request) throws ServletException {
    // Check whether we should support the request method.
    //当前请求方式
    String method = request.getMethod();
    /**
     * 如果用户设置了supportedMethods属性,表明用户只想支持它指定的方法
     * 比如,如果设置POST,GET,那么将只能处理POST,GET,其他方式的请求过来,就会抛出异常
     */
    if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
        throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
    }

    // Check whether a session is required.
    /**
     * request.getSession(false)获取当前请求对应的session,
     * false表示如果不存在不会创建新的session
     */
    if (this.requireSession && request.getSession(false) == null) {
        throw new HttpSessionRequiredException("Pre-existing session required but none found");
    }
}

检查是否支持该请求方式

检查该请求是否必须存在session

8.2 invokeHandlerMethod()方法,执行处理器方法并返回一个ModelAndView对象

//忽略重定向时的默认模型
private boolean ignoreDefaultModelOnRedirect = false;

/**
 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
 * if view resolution is required.
 * @since 4.2
 * @see #createInvocableHandlerMethod(HandlerMethod)
 */
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                           HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    //创建一个请求的适配器对象,见11
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        //获取数据绑定器工厂,见8.2.1
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        //获取模型工厂,见8.2.2
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        //扩展HandlerMethod,使其支持处理方法返回值,见8.2.3
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        //参数解析器
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        //返回值处理器
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        //创建模型视图容器,见15
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        /**
         * RequestContextUtils.getInputFlashMap(request)方法会得到
         * org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP
         * 中的域属性值,见15.1
         * 而addAllAttributes()方法则会将上面得到的域属性值保存到模型中,见15.2
         */
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        //初始化模型,见14.3
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        //设置忽略重定向时的默认模型
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        /***************************异步web请求start*********************************/
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);
        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();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }
        /*****************************end***************************************/

        /**
         * 执行处理器方法和处理返回值
         * 
         * 这里面又是一大段的逻辑,我们放到下一篇文章讲解
         * 见springmvc--5--ServletInvocableHandlerMethod增强的处理器方法
         */
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        //获取ModelAndView对象,见8.2.4
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        //设置请求已处理完成,见11.1
        webRequest.requestCompleted();
    }
}

该方法是核心方法,它会完成请求处理的大部分工作

  • 创建数据绑定器工厂(解析@InitBinder注解)
  • 创建模型工厂(解析@ModelAttribute注解)
  • 解析@SessionAttributes注解
  • 解析处理器方法参数
  • 调用处理器方法
  • 解析处理器方法的返回值
  • 创建对应的ModelAndView对象

8.2.1 getDataBinderFactory(handlerMethod)方法, 获取数据绑定器工厂

//缓存有@InitBinder注解的方法
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(84);

//方法过滤器,过滤得到有@InitBinder注解的方法
public static final MethodFilter INIT_BINDER_METHODS = method ->
    AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);

/**
 * 全局方法,实际上就是@ControllerAdvice注解类中有@InitBinder注解的方法,
 * 见5.1,搜索容器中@ControllerAdvice注解类中有@InitBinder注解的方法
 * 这种方式配置数据绑定器对所有的处理器方法生效
 */
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();


private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
    //获取处理器方法所在类的clazz对象
    Class<?> handlerType = handlerMethod.getBeanType();
    //先从缓存中获取该类拥有@InitBinder注解的方法对象
    Set<Method> methods = this.initBinderCache.get(handlerType);
    //第一次,使用内省得到所有有@InitBinder注解的方法,并放入缓存中
    if (methods == null) {
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        this.initBinderCache.put(handlerType, methods);
    }
    
    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
    // Global methods first
    //首先是全局@InitBinder注解方法,遍历
    this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
        //判断@ControllerAdvice中的方法是否对该处理器方法生效,见9.3
        if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
            //获取ControllerAdviceBean对应的bean对象,见9.4
            Object bean = controllerAdviceBean.resolveBean();
            for (Method method : methodSet) {
                //创建并初始化一个绑定器方法,见8.2.1.1
                initBinderMethods.add(createInitBinderMethod(bean, method));
            }
        }
    });
    
    //其次是拥有@InitBinder注解的方法
    for (Method method : methods) {
        //获取处理器方法所在类的对象
        Object bean = handlerMethod.getBean();
        //创建并初始化一个绑定器方法,见8.2.1.1
        initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    //创建数据绑定工厂,见8.2.1.2
    return createDataBinderFactory(initBinderMethods);
}

总结:

  • 首先将全局@InitBinder注解方法和其所在类对象封装为InvocableHandlerMethod
  • 接下来将当前处理器方法所在类对象和这个对象中的@InitBinder注解方法封装为InvocableHandlerMethod
  • 根据这些InvocableHandlerMethod构建一个ServletRequestDataBinderFactory工厂对象,用来创建ServletRequestDataBinder数据绑定器对象
8.2.1.1 创建并初始化一个绑定器方法InvocableHandlerMethod
//默认的参数名发现器
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

/**
 * 默认为ConfigurableWebBindingInitializer
 * springmvc配置文件中使用`<mvc:annotation-driven>`,会自动向该属性中填充一个对象
 */
@Nullable
private WebBindingInitializer webBindingInitializer;


private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
    /**
     * 可调用的处理器方法,扩展了HandlerMethod
     * 添加了解析处理器方法参数和数据绑定器工厂的功能
     */
    InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
    /**
     * 设置绑定器的参数解析器
     * 类型为HandlerMethodArgumentResolverComposite
     */
    if (this.initBinderArgumentResolvers != null) {
        binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
    }
    /**
     * 设置数据绑定器工厂
     * 类型为DefaultDataBinderFactory
     */
    binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
    //设置参数名发现器
    binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    return binderMethod;
}
8.2.1.2 创建数据绑定器工厂ServletRequestDataBinderFactory
/**
 * Template method to create a new InitBinderDataBinderFactory instance.
 * <p>The default implementation creates a ServletRequestDataBinderFactory.
 * This can be overridden for custom ServletRequestDataBinder subclasses.
 * @param binderMethods {@code @InitBinder} methods
 * @return the InitBinderDataBinderFactory instance to use
 * @throws Exception in case of invalid state or arguments
 */
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
      throws Exception {

    //具体的构造过程见12.1
   return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}

8.2.2 getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory)方法,获取模型工厂

//缓存controller中没有@RequestMapping注解且有@ModelAttribute注解的方法列表
private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);

//过滤得到没有@RequestMapping注解且有@ModelAttribute注解的方法
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
    (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
     AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));


private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
    //获取session属性处理器,见8.2.2.1
    SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
    //获取处理器方法所在类的clazz对象
    Class<?> handlerType = handlerMethod.getBeanType();
    //下面实际上就是得到有特定条件的方法,并缓存
    Set<Method> methods = this.modelAttributeCache.get(handlerType);
    if (methods == null) {
        methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
        //缓存
        this.modelAttributeCache.put(handlerType, methods);
    }
    List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
    // Global methods first
    //全局方法 @ControllerAdvice注解中的方法
    this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
        if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
            Object bean = controllerAdviceBean.resolveBean();
            for (Method method : methodSet) {
                //创建并初始化一个模型属性方法,见8.2.2.2
                attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
            }
        }
    });
    //当前处理器方法所在类中的方法
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        //创建并初始化一个模型属性方法,见8.2.2.2
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
    }
    //创建模型工厂,见14.2
    return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

总结该方法的流程大致如下所示:

  • 首先得到当前处理器方法所在类的会话属性处理器SessionAttributesHandler
  • 得到当前处理器方法所在类中没有@RequestMapping注解且有@ModelAttribute注解的方法列表
  • 创建并初始化一个模型属性方法(再次封装方法列表中的每一个方法)
  • 创建模型工厂对象
8.2.2.1 获取session属性处理器
/**
 * session属性处理器缓存
 * 保存了每一个controller中@SessionAttributes注解信息
 */
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);

//session属性操作策略
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();


/**
 * Return the {@link SessionAttributesHandler} instance for the given handler type
 * (never {@code null}).
 */
private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
    
    //是以controller的clazz对象为键存储的
    return this.sessionAttributesHandlerCache.computeIfAbsent(
        handlerMethod.getBeanType(),
        type -> new SessionAttributesHandler(type, this.sessionAttributeStore));
}
8.2.2.2 创建并初始化一个模型属性方法InvocableHandlerMethod
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
    InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
    /**
     * 设置模型属性方法的参数解析器
     * 类型为HandlerMethodArgumentResolverComposite
     */
    if (this.argumentResolvers != null) {
        attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    /**
     * 设置模型属性方法的参数名发现器
     * 类型为DefaultParameterNameDiscoverer
     */
    attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    /**
     * 设置模型属性方法的数据绑定器工厂
     * 类型为ServletRequestDataBinderFactory,见8.2.1.2
     */
    attrMethod.setDataBinderFactory(factory);
    return attrMethod;
}

8.2.3 创建ServletInvocableHandlerMethod

/**
 * Create a {@link ServletInvocableHandlerMethod} from the given {@link HandlerMethod} definition.
 * @param handlerMethod the {@link HandlerMethod} definition
 * @return the corresponding {@link ServletInvocableHandlerMethod} (or custom subclass thereof)
 * @since 4.2
 */
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
    return new ServletInvocableHandlerMethod(handlerMethod);
}

只是简单的将HandlerMethod处理器方法包装为一个web环境的处理器方法ServletInvocableHandlerMethod

8.2.4 获取ModelAndView对象

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                                     ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    /**
     * 将模型中与用户通过@SessionAttributes注解声明的匹配的属性存储到session域中
     * 有必要的话就将数据绑定属性保存到模型中,见14.6
     */
    modelFactory.updateModel(webRequest, mavContainer);
    //请求已被完全处理
    if (mavContainer.isRequestHandled()) {
        return null;
    }
    /**
     * 获取当前使用的模型,见15.2
     * 重定向使用重定向模型,否则使用默认模型
     */
    ModelMap model = mavContainer.getModel();
    //初始化一个ModelAndView对象,见16.1
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    //非视图名,即视图对象
    if (!mavContainer.isViewReference()) {
        //强转为视图对象并设置到ModelAndView中
        mav.setView((View) mavContainer.getView());
    }
    
    //重定向
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}
  • 对于需要响应视图的请求,在此方法中会创建一个ModelAndView对象,同时保存模型数据,视图和响应状态

  • 对于不需要响应视图的请求,比如有@ResponseBody注解,就直接返回null

9 ControllerAdviceBean类,封装@ControllerAdvice注解的bean

下面是属性

public class ControllerAdviceBean implements Ordered {

    /**
     * Reference to the actual bean instance or a {@code String} representing
     * the bean name.
     */
    private final Object beanOrName;

    private final boolean isSingleton;

    /**
     * Reference to the resolved bean instance, potentially lazily retrieved
     * via the {@code BeanFactory}.
     */
    @Nullable
    private Object resolvedBean;

    @Nullable
    private final Class<?> beanType;

    private final HandlerTypePredicate beanTypePredicate;

    @Nullable
    private final BeanFactory beanFactory;

    @Nullable
    private Integer order;
}

9.1 构造方法

/**
 * Create a {@code ControllerAdviceBean} using the given bean name,
 * {@code BeanFactory}, and {@link ControllerAdvice @ControllerAdvice}
 * annotation.
 * @param beanName the name of the bean
 * @param beanFactory a {@code BeanFactory} to retrieve the bean type initially
 * and later to resolve the actual bean
 * @param controllerAdvice the {@code @ControllerAdvice} annotation for the
 * bean, or {@code null} if not yet retrieved
 * @since 5.2
 */
public ControllerAdviceBean(String beanName, BeanFactory beanFactory, @Nullable ControllerAdvice controllerAdvice) {
    Assert.hasText(beanName, "Bean name must contain text");
    Assert.notNull(beanFactory, "BeanFactory must not be null");
    Assert.isTrue(beanFactory.containsBean(beanName), () -> "BeanFactory [" + beanFactory +
                  "] does not contain specified controller advice bean '" + beanName + "'");
    //保存beanName
    this.beanOrName = beanName;
    //是否单例
    this.isSingleton = beanFactory.isSingleton(beanName);
    //bean的类型
    this.beanType = getBeanType(beanName, beanFactory);
    //bean对象类型断言
    this.beanTypePredicate = (controllerAdvice != null ? createBeanTypePredicate(controllerAdvice) :
                              createBeanTypePredicate(this.beanType));
    this.beanFactory = beanFactory;
}

//根据@ControllerAdvice注解信息构建一个HandlerTypePredicate
private static HandlerTypePredicate createBeanTypePredicate(@Nullable ControllerAdvice controllerAdvice) {
    if (controllerAdvice != null) {
        return HandlerTypePredicate.builder()
            .basePackage(controllerAdvice.basePackages())
            .basePackageClass(controllerAdvice.basePackageClasses())
            .assignableType(controllerAdvice.assignableTypes())
            .annotation(controllerAdvice.annotations())
            .build();
    }
    return HandlerTypePredicate.forAnyHandlerType();
}

该构造方法解析得到@ControllerAdvice注解的bean的名字和类型,并创建对应的HandlerTypePredicate

9.2 获取容器中有@ControllerAdvice注解的bean,并封装为ControllerAdviceBean

/**
 * Find beans annotated with {@link ControllerAdvice @ControllerAdvice} in the
 * given {@link ApplicationContext} and wrap them as {@code ControllerAdviceBean}
 * instances.
 * <p>As of Spring Framework 5.2, the {@code ControllerAdviceBean} instances
 * in the returned list are sorted using {@link OrderComparator#sort(List)}.
 * @see #getOrder()
 * @see OrderComparator
 * @see Ordered
 */
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
    List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
    //获取容器中所有bean的名字并遍历
    for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
        //非代理类(没有使用作用域代理模式,@Scope注解指定)
        if (!ScopedProxyUtils.isScopedTarget(name)) {
            //得到bean中@ControllerAdvice注解信息
            ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
            //有@ControllerAdvice注解
            if (controllerAdvice != null) {
                // Use the @ControllerAdvice annotation found by findAnnotationOnBean()
                // in order to avoid a subsequent lookup of the same annotation.
                //构建ControllerAdviceBean,见9.1
                adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
            }
        }
    }
    //根据PriorityOrdered接口和Order接口排序
    OrderComparator.sort(adviceBeans);
    return adviceBeans;
}

该方法会获取到容器中所有@ControllerAdvice注解的bean,并封装为ControllerAdviceBean,然后根据PriorityOrdered接口和Order接口排序。

9.3 判断@ControllerAdvice中的方法是否对该处理器方法所在类生效

/**
 * Check whether the given bean type should be advised by this
 * {@code ControllerAdviceBean}.
 * @param beanType the type of the bean to check
 * @since 4.0
 * @see ControllerAdvice
 */
public boolean isApplicableToBeanType(@Nullable Class<?> beanType) {
    //使用断言器,判断是否对该类生效,见10.2
    return this.beanTypePredicate.test(beanType);
}

9.4 获取ControllerAdviceBean对应的bean对象

/**
 * Get the bean instance for this {@code ControllerAdviceBean}, if necessary
 * resolving the bean name through the {@link BeanFactory}.
 * <p>As of Spring Framework 5.2, once the bean instance has been resolved it
 * will be cached if it is a singleton, thereby avoiding repeated lookups in
 * the {@code BeanFactory}.
 */
public Object resolveBean() {
    if (this.resolvedBean == null) {
        // this.beanOrName must be a String representing the bean name if
        // this.resolvedBean is null.
        //调用工厂的getBean()方法实例化
        Object resolvedBean = obtainBeanFactory().getBean((String) this.beanOrName);
        // Don't cache non-singletons (e.g., prototypes).
        //非单例的就不缓存
        if (!this.isSingleton) {
            return resolvedBean;
        }
        //单例的缓存
        this.resolvedBean = resolvedBean;
    }
    return this.resolvedBean;
}

10 HandlerTypePredicate,断言器,用来匹配请求处理组件的类型

它是专门用来匹配@ControllerAdvice注解指定类的一个断言器,它有一个嵌套类Builder(建造者模式),方便创建一个HandlerTypePredicate对象。

public final class HandlerTypePredicate implements Predicate<Class<?>> {
    //包名
    private final Set<String> basePackages;
    //类型
    private final List<Class<?>> assignableTypes;
    //注解
    private final List<Class<? extends Annotation>> annotations;
}

很明显,它的三个属性分别对应的@ControllerAdvice注解中的三个属性,这3个属性用来指定@ControllerAdvice注解类中方法的生效类,比如我们常用的@ExceptionHandler(全局异常处理器),可以在ControllerAdvice注解中指定能够捕获哪些类抛出的异常。

并且它实现了spring定义的Predicate断言接口,所以它的核心方法是test()方法。

10.1 嵌套类Builder(建造者模式)

public static class Builder {

    //这三个属性分别对应的@ControllerAdvice注解中的三个 
    private final Set<String> basePackages = new LinkedHashSet<>();
   
    private final List<Class<?>> assignableTypes = new ArrayList<>();
   
    private final List<Class<? extends Annotation>> annotations = new ArrayList<>();

    /**
     * Match handlers declared under a base package, e.g. "org.example".
     * @param packages one or more base package classes
     */
    public Builder basePackage(String... packages) {
        Arrays.stream(packages).filter(StringUtils::hasText).forEach(this::addBasePackage);
        return this;
    }

    /**
     * Type-safe alternative to {@link #forBasePackage(String...)} to specify a
     * base package through a class.
     * @param packageClasses one or more base package names
     */
    public Builder basePackageClass(Class<?>... packageClasses) {
        Arrays.stream(packageClasses).forEach(clazz -> addBasePackage(ClassUtils.getPackageName(clazz)));
        return this;
    }

    private void addBasePackage(String basePackage) {
        this.basePackages.add(basePackage.endsWith(".") ? basePackage : basePackage + ".");
    }

    /**
     * Match handlers that are assignable to a given type.
     * @param types one or more handler super types
     */
    public Builder assignableType(Class<?>... types) {
        this.assignableTypes.addAll(Arrays.asList(types));
        return this;
    }

    /**
     * Match types that are annotated with one of the given annotations.
     * @param annotations one or more annotations to check for
     */
    @SuppressWarnings("unchecked")
    public final Builder annotation(Class<? extends Annotation>... annotations) {
        this.annotations.addAll(Arrays.asList(annotations));
        return this;
    }

    //最后这个build()方法中创建一个HandlerTypePredicate对象
    public HandlerTypePredicate build() {
        return new HandlerTypePredicate(this.basePackages, this.assignableTypes, this.annotations);
    }
}

这就是9.1中使用的一种类似于Stream的原理

10.2 test()方法(Predicate断言接口的唯一方法)

@Override
public boolean test(Class<?> controllerType) {
    /**
     * 未在@ControllerAdvice注解中指定生效类
     * 说明对所有的类都生效
     */
    if (!hasSelectors()) {
        return true;
    }
    
    //部分类生效,进行三种方式的判断
    else if (controllerType != null) {
        //包名
        for (String basePackage : this.basePackages) {
            if (controllerType.getName().startsWith(basePackage)) {
                return true;
            }
        }
        //类型
        for (Class<?> clazz : this.assignableTypes) {
            if (ClassUtils.isAssignable(clazz, controllerType)) {
                return true;
            }
        }
        //注解
        for (Class<? extends Annotation> annotationClass : this.annotations) {
            if (AnnotationUtils.findAnnotation(controllerType, annotationClass) != null) {
                return true;
            }
        }
    }
    return false;
}


//判断是否配置@ControllerAdvice注解属性
private boolean hasSelectors() {
   return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty());
}

11 ServletWebRequest类,适配HttpServletRequest

下面是它的类图

在这里插入图片描述

我们再来看一下这3个接口提供的方法

在这里插入图片描述

  • RequestAttributes接口,它提供了对请求域属性操作的方法和获取Session信息的相关方法。它的实现类ServletRequestAttributes持有一个HttpServletRequestHttpServletResponseHttpSession对象,这些接口方法的实现最终是借助原生的HttpServletRequest方法实现的。
  • WebRequest接口,进一步扩展,它提供了很多方法,比如访问请求参数、请求头信息、请求描述、上下文路径,这些都可以通过HttpServletRequest对象的原生方法得到
  • NativeWebRequest接口,提供获取本次请求响应对象的方法,后面两种方法可以获取指定请求响应对象的上级包装对象

下面是getNativeRequest()方法内部实现

/**
 * Return an appropriate request object of the specified type, if available,
 * unwrapping the given request as far as necessary.
 * @param request the servlet request to introspect
 * @param requiredType the desired type of request object
 * @return the matching request object, or {@code null} if none
 * of that type is available
 */
@SuppressWarnings("unchecked")
@Nullable
public static <T> T getNativeRequest(ServletRequest request, @Nullable Class<T> requiredType) {
    if (requiredType != null) {
        if (requiredType.isInstance(request)) {
            return (T) request;
        }
        else if (request instanceof ServletRequestWrapper) {
            //递归,获取匹配的类型
            return getNativeRequest(((ServletRequestWrapper) request).getRequest(), requiredType);
        }
    }
    return null;
}

11.1 requestCompleted()方法,设置请求已完成

该方法定义在AbstractRequestAttributes类中

//请求是否活跃,完成了就不活跃为false
private volatile boolean requestActive = true;

/**
 * Signal that the request has been completed.
 * <p>Executes all request destruction callbacks and updates the
 * session attributes that have been accessed during request processing.
 */
public void requestCompleted() {
    executeRequestDestructionCallbacks();
    updateAccessedSessionAttributes();
    //设置本次请求状态为已完成
    this.requestActive = false;
}

12 WebDataBinderFactory数据绑定器工厂

默认使用的是ServletRequestDataBinderFactory,它的类图如下

在这里插入图片描述

我们看一下WebDataBinderFactory接口

public interface WebDataBinderFactory {

    /**
     * Create a {@link WebDataBinder} for the given object.
     * @param webRequest the current request
     * @param target the object to create a data binder for,
     * or {@code null} if creating a binder for a simple type
     * @param objectName the name of the target object
     * @return the created {@link WebDataBinder} instance, never null
     * @throws Exception raised if the creation and initialization of the data binder fails
     */
    WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
        throws Exception;

}

很明显,这是一个工厂接口,它用来创建一个WebDataBinder数据绑定器对象

12.1 ServletRequestDataBinderFactory的构造方法

/**
 * Create a new instance.
 * @param binderMethods one or more {@code @InitBinder} methods
 * @param initializer provides global data binder initialization
 */
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
                                       @Nullable WebBindingInitializer initializer) {

    super(binderMethods, initializer);
}

父类InitBinderDataBinderFactory构造方法

//保存有@InitBinder注解的方法
private final List<InvocableHandlerMethod> binderMethods;

/**
 * Create a new InitBinderDataBinderFactory instance.
 * @param binderMethods {@code @InitBinder} methods
 * @param initializer for global data binder initialization
 */
public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
      @Nullable WebBindingInitializer initializer) {

   super(initializer);
   this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
}

父类DefaultDataBinderFactory构造方法

/**
 * 绑定器的初始化器
 * 使用这个初始化器来初始化绑定器
 */
@Nullable
private final WebBindingInitializer initializer;


/**
 * Create a new {@code DefaultDataBinderFactory} instance.
 * @param initializer for global data binder initialization
 * (or {@code null} if none)
 */
public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) {
   this.initializer = initializer;
}

构造方法只是简单的将两个参数对象赋值给属性

12.2 createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)方法,创建属性对应的数据绑定器

/**
 * 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
@SuppressWarnings("deprecation")
public final WebDataBinder createBinder(
    NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {

    //创建数据绑定器实例,类型为ExtendedServletRequestDataBinder
    WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
    //初始化器初始化数据绑定器
    if (this.initializer != null) {
        this.initializer.initBinder(dataBinder, webRequest);
    }
    //初始化数据绑定器
    initBinder(dataBinder, webRequest);
    return dataBinder;
}

13 SessionAttributesHandler 会话属性处理器

管理@SessionAttributes注解声明的属性,实际过程委派给SessionAttributeStore实例完成

下面是SessionAttributesHandler类的属性

public class SessionAttributesHandler {

    //session属性名
    private final Set<String> attributeNames = new HashSet<>();

    //session属性类型
    private final Set<Class<?>> attributeTypes = new HashSet<>();

    //已知的属性名
    private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));

    //session属性存储策略
    private final SessionAttributeStore sessionAttributeStore;
}

13.1 SessionAttributeStore策略接口,定义操作session域属性的规范

public interface SessionAttributeStore {

    /**
     * 存储属性要session域中
     * @param request 当前请求
     * @param attributeName 属性名
     * @param attributeValue 属性值
     */
    void storeAttribute(WebRequest request, String attributeName, Object attributeValue);

    /**
     * 获取session域attributeName对应属性值
     */
    @Nullable
    Object retrieveAttribute(WebRequest request, String attributeName);

    /**
     * 清除session域attributeName对应属性
     */
    void cleanupAttribute(WebRequest request, String attributeName);

}

很明显,这个接口定义了操作session域属性的规范,策略模式主要是为了解耦。我们可以看一下springmvc对该接口的默认实现

@Override
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
    Assert.notNull(request, "WebRequest must not be null");
    Assert.notNull(attributeName, "Attribute name must not be null");
    Assert.notNull(attributeValue, "Attribute value must not be null");
    String storeAttributeName = getAttributeNameInSession(request, attributeName);
    //最终借助了原生request的setAttribute()方法,完成session属性存储
    request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
}

@Override
@Nullable
public Object retrieveAttribute(WebRequest request, String attributeName) {
    Assert.notNull(request, "WebRequest must not be null");
    Assert.notNull(attributeName, "Attribute name must not be null");
    String storeAttributeName = getAttributeNameInSession(request, attributeName);
     //最终借助了原生request的getAttribute()方法,完成session属性获取
    return request.getAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
}

13.2 SessionAttributesHandler的构造方法

/**
 * Create a new session attributes handler. Session attribute names and types
 * are extracted from the {@code @SessionAttributes} annotation, if present,
 * on the given type.
 * @param handlerType the controller type
 * @param sessionAttributeStore used for session access
 */
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
    Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
    this.sessionAttributeStore = sessionAttributeStore;

    //找到处理器方法所在类上@SessionAttributes注解信息
    SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
    //将注解上的属性信息保存到会话属性处理器中
    if (ann != null) {
        Collections.addAll(this.attributeNames, ann.names());
        Collections.addAll(this.attributeTypes, ann.types());
    }
    this.knownAttributeNames.addAll(this.attributeNames);
}
  • 构造方法会获取到对应类上的@SessionAttributes注解信息,然后将用户设置在注解上的信息保存到SessionAttributesHandler会话属性处理器中。
  • @SessionAttributes注解是用来声明session域中必须存在的属性。

13.3 retrieveAttributes(WebRequest request)方法,得到session中的已知属性的值

/**
 * Retrieve "known" attributes from the session, i.e. attributes listed
 * by name in {@code @SessionAttributes} or attributes previously stored
 * in the model that matched by type.
 * @param request the current request
 * @return a map with handler session attributes, possibly empty
 */
public Map<String, Object> retrieveAttributes(WebRequest request) {
    Map<String, Object> attributes = new HashMap<>();
    for (String name : this.knownAttributeNames) {
        Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
        if (value != null) {
            attributes.put(name, value);
        }
    }
    return attributes;
}

该方法会得到@SessionAttributes注解声明的session域属性键值对。

13.4 isHandlerSessionAttribute(String attributeName, Class<?> attributeType)方法,判断属性名称或属性类型是否与用户通过@SessionAttributes注解指定的属性匹配

/**
 * Whether the attribute name or type match the names and types specified
 * via {@code @SessionAttributes} on the underlying controller.
 * <p>Attributes successfully resolved through this method are "remembered"
 * and subsequently used in {@link #retrieveAttributes(WebRequest)} and
 * {@link #cleanupAttributes(WebRequest)}.
 * @param attributeName the attribute name to check
 * @param attributeType the type for the attribute
 */
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
    Assert.notNull(attributeName, "Attribute name must not be null");
    //属性名或属性类型
    if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
        this.knownAttributeNames.add(attributeName);
        return true;
    }
    else {
        return false;
    }
}

判断属性名称或属性类型是否与用户通过@SessionAttributes注解指定的属性匹配

13.5 cleanupAttributes(WebRequest request)方法,清理会话属性

/**
 * Remove "known" attributes from the session, i.e. attributes listed
 * by name in {@code @SessionAttributes} or attributes previously stored
 * in the model that matched by type.
 * @param request the current request
 */
public void cleanupAttributes(WebRequest request) {
   for (String attributeName : this.knownAttributeNames) {
      this.sessionAttributeStore.cleanupAttribute(request, attributeName);
   }
}

删除所有@SessionAttributes注解声明的session域属性

13.6 storeAttributes(WebRequest request, Map<String, ?> attributes)方法,将模型中与用户@SessionAttributes注解声明的匹配的属性存储到session域中

/**
 * Store a subset of the given attributes in the session. Attributes not
 * declared as session attributes via {@code @SessionAttributes} are ignored.
 * @param request the current request
 * @param attributes candidate attributes for session storage
 */
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
    attributes.forEach((name, value) -> {
        //存储之前会过滤
        if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
            this.sessionAttributeStore.storeAttribute(request, name, value);
        }
    });
}

只会将模型中与用户通过@SessionAttributes注解声明的匹配的属性存储到session域中

14 ModelFactory模型工厂

作用:

  • 在控制器方法调用之前初始化Model,会将@ModelAttribute注解指定属性保存到Model
  • 在控制器方法调用之后更新Model数据

首先看一下它的属性

public final class ModelFactory {

    //模型方法
    private final List<ModelMethod> modelMethods = new ArrayList<>();

    //数据绑定器工厂
    private final WebDataBinderFactory dataBinderFactory;

    //session属性处理器
    private final SessionAttributesHandler sessionAttributesHandler;
}

14.1 ModelMethod 模型方法

该类是ModelFactory的嵌套类

private static class ModelMethod {

    //模型属性方法
    private final InvocableHandlerMethod handlerMethod;

    //@ModelAttribute注解标注的参数名
    private final Set<String> dependencies = new HashSet<>();

    public ModelMethod(InvocableHandlerMethod handlerMethod) {
        this.handlerMethod = handlerMethod;
        /**
         * 遍历模型属性方法的参数
         * 参数上有@ModelAttribute注解就将参数名保存到dependencies属性中
         */
        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
            if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
                this.dependencies.add(getNameForParameter(parameter));
            }
        }
    }

    public InvocableHandlerMethod getHandlerMethod() {
        return this.handlerMethod;
    }

    //判断视图模型容器中是否存在@ModelAttribute注解标注的参数名
    public boolean checkDependencies(ModelAndViewContainer mavContainer) {
        for (String name : this.dependencies) {
            if (!mavContainer.containsAttribute(name)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public String toString() {
        return this.handlerMethod.getMethod().toGenericString();
    }
}

@ModelAttribute既可以标注在方法上又可以标注在方法参数

该类会对在方法上标注@ModelAttribute注解的方法进行初步处理,它会将方法参数中有@ModelAttribute注解的参数名解析并封装到属性中

14.2 构造方法

/**
 * Create a new instance with the given {@code @ModelAttribute} methods.
 * @param handlerMethods the {@code @ModelAttribute} methods to invoke
 * @param binderFactory for preparation of {@link BindingResult} attributes
 * @param attributeHandler for access to session attributes
 */
public ModelFactory(@Nullable List<InvocableHandlerMethod> handlerMethods,
                    WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {

    if (handlerMethods != null) {
        for (InvocableHandlerMethod handlerMethod : handlerMethods) {
            this.modelMethods.add(new ModelMethod(handlerMethod));
        }
    }
    this.dataBinderFactory = binderFactory;
    this.sessionAttributesHandler = attributeHandler;
}

构造方法没做什么事情,只是简单的为@ModelAttribute注解方法创建一个ModelMethod方法实例,另外给其他属性初始化一些值

14.3 initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)方法,初始化模型

/**
 * Populate the model in the following order:
 * <ol>
 * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}.
 * <li>Invoke {@code @ModelAttribute} methods
 * <li>Find {@code @ModelAttribute} method arguments also listed as
 * {@code @SessionAttributes} and ensure they're present in the model raising
 * an exception if necessary.
 * </ol>
 * @param request the current request
 * @param container a container with 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 container, HandlerMethod handlerMethod)
    throws Exception {

    //得到@SessionAttributes注解声明的所有session域属性键值对,见13.3
    Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
    /**
     * 合并模型数据,见15.3
     * 合并request和session域属性,之前在15.1中得到了特定的request域属性
     * 现在在13.3中有得到了所有@SessionAttributes注解声明的所有session域属性
     * 在此处进行两个的合并
     */
    container.mergeAttributes(sessionAttributes);
    //执行模型属性方法,见14.4
    invokeModelAttributeMethods(request, container);

    
    //获取模型方法所有中Session属性参数名
    for (String name : findSessionAttributeArguments(handlerMethod)) {
        //模型视图容器中不包含该参数名
        if (!container.containsAttribute(name)) {
            //取得参数名对应的session属性值
            Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
            if (value == null) {
                throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
            }
            //保存到模型视图容器中
            container.addAttribute(name, value);
        }
    }
}

该方法主要作用是填充模型属性,按如下顺序

  • @SessionAttributes注解声明的所有session域属性
  • 模型属性方法的返回值
  • 模型方法中所有匹配@SessionAttributes注解声明的属性的标注@ModelAttribute注解参数

14.4 invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)方法,执行模型属性方法

/**
 * 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 container)
    throws Exception {

    //遍历执行所有@ModelAttribute注解方法(包括全局的)
    while (!this.modelMethods.isEmpty()) {
        //顺序获取模型方法
        InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
        //得到方法上的@ModelAttribute注解信息
        ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
        Assert.state(ann != null, "No ModelAttribute annotation");
        //模型视图容器中包含用户在的@ModelAttribute注解name属性指定的名字
        if (container.containsAttribute(ann.name())) {
            //@ModelAttribute注解上声明关闭数据绑定
            if (!ann.binding()) {
                //@ModelAttribute注解上声明的所有属性都禁用数据绑定
                container.setBindingDisabled(ann.name());
            }
            continue;
        }

        //执行模型方法,见14.4.2
        Object returnValue = modelMethod.invokeForRequest(request, container);
        //模型方法有返回值
        if (!modelMethod.isVoid()){
            //获取方法返回值的名字,见14.4.3
            String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
            //该返回值禁用数据绑定
            if (!ann.binding()) {
                container.setBindingDisabled(returnValueName);
            }
            //返回值保存到模型中
            if (!container.containsAttribute(returnValueName)) {
                container.addAttribute(returnValueName, returnValue);
            }
        }
    }
}

14.4.1 顺序获取模型方法

private ModelMethod getNextModelMethod(ModelAndViewContainer container) {
    for (ModelMethod modelMethod : this.modelMethods) {
        /**
         * checkDependencies(container)方法见14.1
         * 会把模型方法参数上有@ModelAttribute注解的参数和视图模型容器中的属性一一比较
         * 只要有一个标注了@ModelAttribute注解的参数在视图模型容器中找不到同名属性,就返回false
         * 说明该模型方法还未执行过,需要执行.
         * 否则返回true,从列表中移除该标注了@ModelAttribute注解的方法
         */
        if (modelMethod.checkDependencies(container)) {
            this.modelMethods.remove(modelMethod);
            return modelMethod;
        }
    }
    
    //按顺序获取模型方法
    ModelMethod modelMethod = this.modelMethods.get(0);
    this.modelMethods.remove(modelMethod);
    return modelMethod;
}

它会先检查所有模型方法,如果发现这样一个方法(所有标注了@ModelAttribute注解的参数都能在视图模型容器中找同名属性),就删掉方法缓存,然后返回该方法对象。

否则按照顺序获取模型工厂中的模型方法。

14.4.2 执行模型方法

/**
 * Invoke the method after resolving its argument values in the context of the given request.
 * <p>Argument values are commonly resolved through
 * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
 * The {@code providedArgs} 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.
 * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
 * resolved arguments.
 * @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
 * @throws Exception raised if no suitable argument resolver can be found,
 * or if the method raised an exception
 * @see #getMethodArgumentValues
 * @see #doInvoke
 */
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    return doInvoke(args);
}

该方法的源码我们放到下一篇文章再看(springmvc--5--ServletInvocableHandlerMethod增强的处理器方法

我先来简单说一下过程:首先一个一个的解析方法参数,解析时遍历我们在5.2中设置的默认的参数解析器,找到第一个能解析方法参数的解析器,进行解析方法参数,得到参数值。所有参数解析成功后使用反射invoke()方法完成方法调用。

14.4.3 获取方法返回值的名字

/**
 * Derive the model attribute name for the given return value. Results will be
 * based on:
 * <ol>
 * <li>the method {@code ModelAttribute} annotation value
 * <li>the declared return type if it is more specific than {@code Object}
 * <li>the actual return value type
 * </ol>
 * @param returnValue the value returned from a method invocation
 * @param returnType a descriptor for the return type of the method
 * @return the derived name (never {@code null} or empty String)
 */
public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
    //获取模型方法上的@ModelAttribute注解信息
    ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
    /**
     * 有@ModelAttribute注解且指定了返回值的存储名
     * 以@ModelAttribute注解的value属性为key,保存模型方法的返回到模型中
     */
    if (ann != null && StringUtils.hasText(ann.value())) {
        return ann.value();
    }
    //使用默认的名字将返回值保存到模型中
    else {
        Method method = returnType.getMethod();
        Assert.state(method != null, "No handler method");
        Class<?> containingClass = returnType.getContainingClass();
        Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
        return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
    }
}

14.5 findSessionAttributeArguments(HandlerMethod handlerMethod)方法,获取模型方法所有中Session属性参数名

/**
 * Find {@code @ModelAttribute} arguments also listed as {@code @SessionAttributes}.
 */
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
    List<String> result = new ArrayList<>();
    //遍历模型方法参数
    for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
        //参数上有@ModelAttribute注解
        if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
            //参数名
            String name = getNameForParameter(parameter);
            //参数类型
            Class<?> paramType = parameter.getParameterType();
            //参数是@SessionAttributes注解指定的属性
            if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
                result.add(name);
            }
        }
    }
    return result;
}j

该方法会得到模型方法中满足下面条件的参数名

  • 参数上有@ModelAttribute注解
  • 参数名或类型匹配用户@SessionAttributes注解指定的参数名或类型

14.6 updateModel(NativeWebRequest request, ModelAndViewContainer container)方法, 将@SessionAttributes注解声明的属性保存到session

/**
 * Promote model attributes listed as {@code @SessionAttributes} to the session.
 * Add {@link BindingResult} attributes where necessary.
 * @param request the current request
 * @param container contains the model to update
 * @throws Exception if creating BindingResult attributes fails
 */
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
    //得到默认模型,见15.4
    ModelMap defaultModel = container.getDefaultModel();
    //当前会话已完成,见15.5
    if (container.getSessionStatus().isComplete()){
        //清理@SessionAttributes注解声明的会话属性,见13.5
        this.sessionAttributesHandler.cleanupAttributes(request);
    }
    //当前会话未完成
    else {
        //只会将模型中与用户通过@SessionAttributes注解声明的匹配的属性存储到session域中,见13.6
        this.sessionAttributesHandler.storeAttributes(request, defaultModel);
    }
    //请求未被完全处理,且使用默认模型(即非重定向)
    if (!container.isRequestHandled() && container.getModel() == defaultModel) {
        //将需要数据绑定的属性添加到模型中,见14.7
        updateBindingResult(request, defaultModel);
    }
}
  • 该方法会判断会话状态,如果会话已完成,就清理@SessionAttributes注解声明的会话属性,否则将将模型中与用户通过@SessionAttributes注解声明的匹配的属性存储到session域中
  • 如果非重定向且请求为被完全处理,就将数据绑定的属性添加到模型中

14.7 updateBindingResult(NativeWebRequest request, ModelMap model)方法,将数据绑定的属性添加到模型中

/**
 * Add {@link BindingResult} attributes to the model for attributes that require it.
 */
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
    List<String> keyNames = new ArrayList<>(model.keySet());
    for (String name : keyNames) {
        Object value = model.get(name);
        //该属性需要进行数据绑定
        if (value != null && isBindingCandidate(name, value)) {
            //MODEL_KEY_PREFIX="org.springframework.validation.BindingResult."
            String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
            //模型中不包含该数据绑定属性
            if (!model.containsAttribute(bindingResultKey)) {
                //使用数据绑定器工厂创建该属性的数据绑定器,见12.2
                WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
                //保存属性
                model.put(bindingResultKey, dataBinder.getBindingResult());
            }
        }
    }
}


/**
 * Whether the given attribute requires a {@link BindingResult} in the model.
 */
private boolean isBindingCandidate(String attributeName, Object value) {
    /**
     * MODEL_KEY_PREFIX="org.springframework.validation.BindingResult."
     * 以这个开头的属性不需要数据绑定
     */
    if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
        return false;
    }

    //用户通过@SessionAttributes注解指定的属性需要进行数据绑定
    if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, value.getClass())) {
        return true;
    }

    //非集合,非数组,非Map,非简单类型的属性都需要进行数据绑定
    return (!value.getClass().isArray() && !(value instanceof Collection) &&
            !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
}

下面是三种过滤条件,表明需要进行数据绑定的属性

  • org.springframework.validation.BindingResult.开头的属性不需要数据绑定
  • 用户通过@SessionAttributes注解指定的属性需要进行数据绑定
  • 集合,非数组,非Map,非简单类型的属性都需要进行数据绑定

15 ModelAndViewContainer模型视图容器

我们先来看一下它的属性

public class ModelAndViewContainer {

    //忽略重定向时的默认模型
    private boolean ignoreDefaultModelOnRedirect = false;

    //视图名
    @Nullable
    private Object view;

    //默认模型
    private final ModelMap defaultModel = new BindingAwareModelMap();

    //重定向模型
    @Nullable
    private ModelMap redirectModel;

    private boolean redirectModelScenario = false;

    //响应状态码
    @Nullable
    private HttpStatus status;

    private final Set<String> noBinding = new HashSet<>(4);

    //禁用绑定的属性
    private final Set<String> bindingDisabled = new HashSet<>(4);

    //会话状态
    private final SessionStatus sessionStatus = new SimpleSessionStatus();

    //请求是否被完全处理
    private boolean requestHandled = false;
}

15.1 数据来源

/**
 * Return read-only "input" flash attributes from request before redirect.
 * @param request current request
 * @return a read-only Map, or {@code null} if not found
 * @see FlashMap
 */
@SuppressWarnings("unchecked")
@Nullable
public static Map<String, ?> getInputFlashMap(HttpServletRequest request) {
    return (Map<String, ?>) request.getAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE);
}
/**
 * Name of request attribute that holds a read-only {@code Map<String,?>}
 * with "input" flash attributes saved by a previous request, if any.
 * @see org.springframework.web.servlet.support.RequestContextUtils#getInputFlashMap(HttpServletRequest)
 */
//org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";

该方法会得到request中所有域属性名为org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP的值

15.2 addAllAttributes(@Nullable Map<String, ?> attributes)方法,添加数据到视图模型容器中

/**
 * Copy all attributes to the underlying model.
 * A shortcut for {@code getModel().addAllAttributes(Map)}.
 */
public ModelAndViewContainer addAllAttributes(@Nullable Map<String, ?> attributes) {
    //由getModel()方法判断将数据添加到defaultModel还是redirectModel中
    getModel().addAllAttributes(attributes);
    return this;
}

/**
 * Return the model to use -- either the "default" or the "redirect" model.
 * The default model is used if {@code redirectModelScenario=false} or
 * there is no redirect model (i.e. RedirectAttributes was not declared as
 * a method argument) and {@code ignoreDefaultModelOnRedirect=false}.
 */
public ModelMap getModel() {
    //非重定向
    if (useDefaultModel()) {
        return this.defaultModel;
    }
    //重定向
    else {
        if (this.redirectModel == null) {
            this.redirectModel = new ModelMap();
        }
        return this.redirectModel;
    }
}

/**
 * Whether to use the default model or the redirect model.
 */
private boolean useDefaultModel() {
    /**
     * 只有非重定向场景或
     * 重定向模型为null且ignoreDefaultModelOnRedirect属性为false(即忽略重定向)
     */
    return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}

数据会添加到重定向模型或默认模型中

15.3 mergeAttributes(@Nullable Map<String, ?> attributes)方法,合并模型数据

/**
 * Copy attributes in the supplied {@code Map} with existing objects of
 * the same name taking precedence (i.e. not getting replaced).
 * A shortcut for {@code getModel().mergeAttributes(Map<String, ?>)}.
 */
public ModelAndViewContainer mergeAttributes(@Nullable Map<String, ?> attributes) {
    /**
     * 由getModel()方法判断将数据添加到defaultModel还是redirectModel中
     * mergeAttributes(attributes)方法会合并两个map集合的值
     * 注意:同key的直接跳过,不会覆盖
     */
    getModel().mergeAttributes(attributes);
    return this;
}

合并两个map集合

/**
 * Copy all attributes in the supplied {@code Map} into this {@code Map},
 * with existing objects of the same name taking precedence (i.e. not getting
 * replaced).
 */
public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
    if (attributes != null) {
        attributes.forEach((key, value) -> {
            if (!containsKey(key)) {
                put(key, value);
            }
        });
    }
    return this;
}

15.4 getDefaultModel()方法,获取默认模型

/**
 * Return the "default" model created at instantiation.
 * <p>In general it is recommended to use {@link #getModel()} instead which
 * returns either the "default" model (template rendering) or the "redirect"
 * model (redirect URL preparation). Use of this method may be needed for
 * advanced cases when access to the "default" model is needed regardless,
 * e.g. to save model attributes specified via {@code @SessionAttributes}.
 * @return the default model (never {@code null})
 * @since 4.1.4
 */
public ModelMap getDefaultModel() {
    //默认模型
    return this.defaultModel;
}

15.5 getSessionStatus()方法,获取会话状态

/**
 * Return the {@link SessionStatus} instance to use that can be used to
 * signal that session processing is complete.
 */
public SessionStatus getSessionStatus() {
   return this.sessionStatus;
}

默认的SessionStatus的类型为SimpleSessionStatus

我们先来看一下SessionStatus接口方法

public interface SessionStatus {

    /**
     * 设置当前会话已完成
     */
    void setComplete();

    /**
     * 当前会话是否完成
     */
    boolean isComplete();

}

它的简单实现SimpleSessionStatus

public class SimpleSessionStatus implements SessionStatus {

    //默认是false,表示会话未完成
    private boolean complete = false;


    @Override
    public void setComplete() {
        this.complete = true;
    }

    @Override
    public boolean isComplete() {
        return this.complete;
    }
}

15.6 getViewName()方法,获取视图名

/**
 * Return the view name to be resolved by the DispatcherServlet via a
 * ViewResolver, or {@code null} if a View object is set.
 */
@Nullable
public String getViewName() {
   return (this.view instanceof String ? (String) this.view : null);
}

模型视图容器中保存的可能是视图名也可能是视图对象

15.7 isViewReference()方法,判断是不是视图名

/**
 * Whether the view is a view reference specified via a name to be
 * resolved by the DispatcherServlet via a ViewResolver.
 */
public boolean isViewReference() {
   return (this.view instanceof String);
}

16 ModelAndView 模型视图

持有模型和视图,以便控制器可以在单个返回值中同时返回视图和模型

下面是它的属性

public class ModelAndView {

    //视图对象或视图名
    @Nullable
    private Object view;

   //模型
    @Nullable
    private ModelMap model;

    //响应状态
    @Nullable
    private HttpStatus status;

    //是否通过clear()方法清除了该实例
    private boolean cleared = false;
}

16.1 构造方法

/**
 * Create a new ModelAndView given a view name, model, and HTTP status.
 * @param viewName name of the View to render, to be resolved
 * by the DispatcherServlet's ViewResolver
 * @param model a Map of model names (Strings) to model objects
 * (Objects). Model entries may not be {@code null}, but the
 * model Map may be {@code null} if there is no model data.
 * @param status an HTTP status code to use for the response
 * (to be set just prior to View rendering)
 * @since 4.3
 */
public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) {
    this.view = viewName;
    if (model != null) {
        getModelMap().addAllAttributes(model);
    }
    this.status = status;
}


/**
 * Return the underlying {@code ModelMap} instance (never {@code null}).
 */
public ModelMap getModelMap() {
    if (this.model == null) {
        this.model = new ModelMap();
    }
    return this.model;
}

根据模型、视图名、响应状态构建一个模型视图对象

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值