我爱编程

Spring MVC中组件Controller参数

2018-04-06  本文已影响0人  小螺钉12138

1、概念

HandlerMapping通过request找到了Handler,HandlerAdapter是具体用来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法。

2、HandlerAdapter基本结构

image

HandlerAdapter是一个接口实现这个接口的有四个类,其中实现该接口的AbstractHandlerMethodAdapter的子类结构最为复杂,同时也是由于处理的Handler不是固定的格式。下面主要来介绍RequestMappingHandlerAdapter。

具体的请求处理过程可以分为三步

理解参数的绑定之前需要先弄清楚三个问题

参数的来源一共有6种

参数的具体解析是使用HandlerMethodArgumentResolver类型的组件完成的,不同类型的参数使用不同的ArgumentResolver来解析。

3、RequestMappingHandlerAdapter自身结构

3.1 RequestMappingHandlerAdapter创建

RequestMappingHandlerAdapter的创建是在afterPropertiesSet方法中实现,其中主要内容就是初始化argumentResolver、initBinderArgumentResolver、returnValueHandler以及
@ControllerAdvice注释的类相关的modelAttributeAdviceCache、initBinderAdviceCache和responseBodyAdvice这6个属性


public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        //初始化注注释了@ControllerAdvice的类的相关属性
        initControllerAdviceCache();

        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);
        }
    }
    


private void initControllerAdviceCache() {
        if (getApplicationContext() == null) {
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
        }

        //获取到所有注释了@ControllerAdvice的bean
        List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
        
        //根据Order排序
        Collections.sort(beans, new OrderComparator());

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

        for (ControllerAdviceBean bean : beans) {
            //查找了注释了@ModelAttribute而且没有注释@RequestMapping的方法
            Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
            if (!attrMethods.isEmpty()) {
                this.modelAttributeAdviceCache.put(bean, attrMethods);
                logger.info("Detected @ModelAttribute methods in " + bean);
            }
            //查找注释了@InitBinder的方法
            Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
            if (!binderMethods.isEmpty()) {
                this.initBinderAdviceCache.put(bean, binderMethods);
                logger.info("Detected @InitBinder methods in " + bean);
            }
            //查找实现了ResponseBodyAdvice接口的类
            if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
                responseBodyAdviceBeans.add(bean);
                logger.info("Detected ResponseBodyAdvice bean in " + bean);
            }
        }

        if (!responseBodyAdviceBeans.isEmpty()) {
            this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
        }
    }

查看后面三个属性属性的初始化中getDefaultXXX方法

/**
     * 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<HandlerMethodArgumentResolver>();

        // 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()));
        resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
        resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));

        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
        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;
    }
    //方法中将默认的参数处理器全部添加进来

//注:在添加参数解析器时,顺序是无法进行改变的
3.2.RequestMappingHandlerAdapter使用

RequestMappingHandlerAdapter处理请求的入口方法是handlerInternal

@Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    //判断Handler是否有@SessionAttribute注释的参数
        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);
    }
    
/**
     * Check and prepare the given request and response according to the settings 
     * of this generator. Checks for supported methods and a required session,(根据给定的请求属性设置,检查方法和session)
     * and applies the given number of cache seconds.(请求一定时间的缓存时间)
     * @param request current HTTP request
     * @param response current HTTP response
     * @param cacheSeconds positive number of seconds into the future that the
     * response should be cacheable for, 0 to prevent caching
     * @param lastModified if the mapped handler provides Last-Modified support
     * @throws ServletException if the request cannot be handled because a check failed
     */
    protected final void checkAndPrepare(
            HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
            throws ServletException {

        // Check whether we should support the request method.
        String method = request.getMethod();
        if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
            throw new HttpRequestMethodNotSupportedException(
                    method, StringUtils.toStringArray(this.supportedMethods));
        }

        // Check whether a session is required.
        if (this.requireSession) {
            if (request.getSession(false) == null) {
                throw new HttpSessionRequiredException("Pre-existing session required but none found");
            }
        }

        // Do declarative cache control.
        // Revalidate if the controller supports last-modified.
        applyCacheSeconds(response, cacheSeconds, lastModified);
    }
/**
     * Apply the given cache seconds and generate respective HTTP headers.
     * <p>That is, allow caching for the given number of seconds in the
     * case of a positive value, prevent caching if given a 0 value, else
     * do nothing (i.e. leave caching to the client).
     * @param response the current HTTP response
     * @param seconds the (positive) number of seconds into the future that
     * the response should be cacheable for; 0 to prevent caching; and
     * a negative value to leave caching to the client.
     * @param mustRevalidate whether the client should revalidate the resource
     * (typically only necessary for controllers with last-modified support)
     */
    protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
        if (seconds > 0) {
        //给缓存设置过期时间
            cacheForSeconds(response, seconds, mustRevalidate);
        }
        else if (seconds == 0) {
        //阻止使用缓存
            preventCaching(response);
        }
        // Leave caching to the client otherwise.
    }
protected final void cacheForSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
        if (this.useExpiresHeader) {
            // HTTP 1.0 header
            response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + seconds * 1000L);
        }
        if (this.useCacheControlHeader) {
            // HTTP 1.1 header
            String headerValue = "max-age=" + seconds;
            if (mustRevalidate || this.alwaysMustRevalidate) {
                headerValue += ", must-revalidate";
            }
            response.setHeader(HEADER_CACHE_CONTROL, headerValue);
        }
    }
    
protected final void preventCaching(HttpServletResponse response) {
    response.setHeader(HEADER_PRAGMA, "no-cache");
    if (this.useExpiresHeader) {
        // HTTP 1.0 header
        response.setDateHeader(HEADER_EXPIRES, 1L);
    }
    if (this.useCacheControlHeader) {
        // HTTP 1.1 header: "no-cache" is the standard value,
        // "no-store" is necessary to prevent caching on FireFox.
        response.setHeader(HEADER_CACHE_CONTROL, "no-cache");
        if (this.useCacheControlNoStore) {
            response.addHeader(HEADER_CACHE_CONTROL, "no-store");
        }
    }
}

缓存的设置其实就是对Response的Header进行了相应的设置,上面的代码实际含义是,如果有@SessionAttribute注释则阻止使用缓存,否则就什么也不做

invokeHandlerMethod方法初始化了三个类型的变量分别是

/**
     * 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));
        modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();

            if (logger.isDebugEnabled()) {
                logger.debug("Found concurrent result value [" + result + "]");
            }
            requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
        }

        requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }

WebDataBinderFactory主要是用来创建WebDataBinder,它的功能是实现参数和String之间的类型转换,WebDataBinderFactory就是将符合条件的的注释了@InitBinder的方法找出来,这里的InitBinder方法包括两部分:1、注释了@ControllerAdvice的并且符合要求的全局处理器里面的InitBinder方法。2、处理器自身的InitBinder方法

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
        Class<?> handlerType = handlerMethod.getBeanType();
        //检查当前Handler中InitBinder方法是否已经存在缓存中
        Set<Method> methods = this.initBinderCache.get(handlerType);
        if (methods == null) {
        //如果没有则查找并且设置到缓存中
            methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
            this.initBinderCache.put(handlerType, methods);
        }
        //定义保存InitBinder方法的临时变量
        List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
        // Global methods first(首先添加全局变量到initBinderMethods)
        for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache .entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                Object bean = entry.getKey().resolveBean();
                for (Method method : entry.getValue()) {
                    initBinderMethods.add(createInitBinderMethod(bean, method));
                }
            }
        }
        //将当前Handler中的InitBinder方法添加到initBinderMethos
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            initBinderMethods.add(createInitBinderMethod(bean, method));
        }
        return createDataBinderFactory(initBinderMethods);
    }

ModelFactoryz是用来处理Model的,主要包含两个功能:1、在处理器具体处理之前对Model进行初始化2、在处理完请求后对Model参数进行更新。
Model初始化分为三个部分:1、将原来的SessionAttribute中的值设置到Model。2、执行相应注释了@ModelAttribute的方法并将其值设置到Mode。3、处理器中注释了@ModelAttribute的参数如果同时在SessionAttribute中也配置了,而且在mavContainer中还没有值则从全部SessionAttribute中查找出来并设置进去

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
    //获取SessionAttributeHandler
        SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
    //获取处理器的类型
        Class<?> handlerType = handlerMethod.getBeanType();
    //获取处理器类中注释了@ModelAttribute而且没有注释@RequestMapping的类型第一次获取后添加到缓存,以后直接从缓存中获取
        Set<Method> methods = this.modelAttributeCache.get(handlerType);
    //
        if (methods == null) {
            methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
            this.modelAttributeCache.put(handlerType, methods);
        }
        List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
        // Global methods first(首先添加全局的方法)
        for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                Object bean = entry.getKey().resolveBean();
                for (Method method : entry.getValue()) {
                    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
                }
            }
        }
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
        }
        return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
    }

ServletInvocableHandlerMethod继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里面完成

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;
    }

前面invokeHandlerMethod方法中的三个变量弄清楚之后就比较容易理解了,上面三个变量完了之后下面还有三个变量需要处理

新建ModelAndViewContainer类型的mavContainer参数,用于保存Model和View,它贯穿于整个处理过程,然后对mavContainer进行了设置,主要包括三部分内容:1、将FlashMap中的数据设置到Model;2、使用modelFactory将SessionAttribute和注释了@ModelAttribute的方法的参数设置到Model;3、根据配置对ignoreDefaultModelOnRedirect进行了参数设置,这个参数在分析ModelAndViewContainer的时候再详细讲解

    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

执行请求,具体方法是直接调用ServletInvocableHandlerMethod里的invokeAndHandle方法执行

requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

处理完请求后的后置处理,这是在getModelAndView方法中处理的。一共做了三件事:1、调用ModelFactory的updateModel方法更新了Model。2、根据mavContainer创建了ModelAndView。3、如果mavContainer里的model是RedirectAttribute类型,则将其设置到FlashMap

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

        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;
    }

上一篇 下一篇

猜你喜欢

热点阅读