美文网首页
springmvc源码分析-HandlerAdapter原理

springmvc源码分析-HandlerAdapter原理

作者: hello_kd | 来源:发表于2020-11-15 17:24 被阅读0次

    springmvc中HandlerAdapter用于执行具体的Handler,也就是controller,是springmvc中一个特别重要的组件,先来看下接口定义的方法

    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    

    方法的整体逻辑就是解析request的参数,传递给handler,执行后拿到结果后,写入到response

    springmvc内置的HandlerAdapter有好几种,本文来看下最常用的RequestMappingHandlerAdapter,这个对象也是在spring容器初始化后便完成了,定义在WebMVCConfigurationSupport中

    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcValidator") Validator validator) {
    
        RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
        adapter.setContentNegotiationManager(contentNegotiationManager);
        adapter.setMessageConverters(getMessageConverters());
        adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
        adapter.setCustomArgumentResolvers(getArgumentResolvers());
        adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    
        if (jackson2Present) {
            adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
            adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
        }
    
        AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
        configureAsyncSupport(configurer);
        if (configurer.getTaskExecutor() != null) {
            adapter.setTaskExecutor(configurer.getTaskExecutor());
        }
        if (configurer.getTimeout() != null) {
            adapter.setAsyncRequestTimeout(configurer.getTimeout());
        }
        adapter.setCallableInterceptors(configurer.getCallableInterceptors());
        adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
    
        return adapter;
    }
    

    从这可以看出HandlerAdapter主要是维护一些对请求参数和响应对象的数据处理,消息转化器messageConverter,参数解析器argumentResolver,返回值处理器returnValueHandler,这三者的关系等下分析

    protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList<>();
            configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
                addDefaultHttpMessageConverters(this.messageConverters);
            }
            extendMessageConverters(this.messageConverters);
        }
        return this.messageConverters;
    }
    

    这里有个比较重要的方法configureMessageConverters,用于子类重写,用于扩展messageConverters,参数解析器和返回值处理器也是差不多的逻辑,都会有一个扩展方法,给应用程序添加

        protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {
            if (this.argumentResolvers == null) {
                this.argumentResolvers = new ArrayList<>();
                addArgumentResolvers(this.argumentResolvers);
            }
            return this.argumentResolvers;
        }
    
        protected final List<HandlerMethodReturnValueHandler> getReturnValueHandlers() {
            if (this.returnValueHandlers == null) {
                this.returnValueHandlers = new ArrayList<>();
                addReturnValueHandlers(this.returnValueHandlers);
            }
            return this.returnValueHandlers;
        }
    

    如果当前项目存在Jackson,那么还会添加两个BodyAdvice,分别为JsonViewRequestBodyAdvice和JsonViewResponseBodyAdvice,这个在后面的工作流程再介绍这两者的作用

    当生成bean对象后,由于RequestMappingHandlerAdaptor也实现了InitializingBean接口,所以还会执行初始化方法afterPropertiesSet

    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        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);
        }
    }
    

    其中,initControllerAdviceCache方法就是查询容器中注解了@ControllerAdvice的类,然后根据三种情况进行处理,如下

    private void initControllerAdviceCache() {
        if (getApplicationContext() == null) {
            return;
        }
    //查找注解了@ControllerAdvice的bean列表
        List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    
        List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
    
        for (ControllerAdviceBean adviceBean : adviceBeans) {
            Class<?> beanType = adviceBean.getBeanType();
            if (beanType == null) {
                throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
            }
    //查询bean中是否有注解@ModelAttribute的方法
            Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
            if (!attrMethods.isEmpty()) {
                this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
            }
    //查询bean中是否有注解@InitBinder的方法
            Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
            if (!binderMethods.isEmpty()) {
                this.initBinderAdviceCache.put(adviceBean, binderMethods);
            }
    //如果该类是RequestBodyAdvice或者ResponseBodyAdvice的,会维护到HandlerAdapter的成员属性中
            if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
                requestResponseBodyAdviceBeans.add(adviceBean);
            }
        }
    
        if (!requestResponseBodyAdviceBeans.isEmpty()) {
            this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
        }
    }
    

    到这边,RequestMappingHandlerAdapter实例化和初始化完成了,然后添加到spring容器中,需要特别注意的是argumentResolvers 、returnValueHandlers 、messageConverters 、@ControllerAdvice,这几个在执行handler过程中发挥了核心作用。

    下面以一个实际的例子来跟踪下RequestMappingHandlerAdapter的工作流程

    @ResponseBody
        @RequestMapping(method = RequestMethod.POST, value = "/getStudent")
        public GetStudentResp getStudent(@RequestBody GetStudentParam param) {
            Student student = new Student();
            student.setName("hello" + param.getUserId());
            GetStudentResp resp = new GetStudentResp();
            resp.setStudent(student);
            return resp;
        }
    

    当有请求到来时,会先经过DispatcherServlet的doDispatch方法,然后HandMapping会拿到了HandlerExecutionChain对象,接着springmvc会拿到一个HandlerAdapter来执行HandlerExecutionChain的HandlerMethod

        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            if (this.handlerAdapters != null) {
                for (HandlerAdapter adapter : this.handlerAdapters) {
                    if (adapter.supports(handler)) {
                        return adapter;
                    }
                }
            }
            throw new ServletException("No adapter for handler [" + handler +
                    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }
        public final boolean supports(Object handler) {
            return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
        }
    

    当拿到RequestMappingHandlerAdaptor对象后,便根据request执行具体的HandlerMethod了

    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    

    一直跟进handle方法

    @Nullable
        protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
            try {
    //将HandlerMethod包装成ServletInvocableHandlerMethod ,并添加argumentResolvers 和returnValueHandlers 
                ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
                if (this.argumentResolvers != null) {
                    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
                }
                if (this.returnValueHandlers != null) {
                    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
                }
            
    //结果容器
                ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            //根据webRequest 执行invocableMethod,然后将结果存储在mavContainer 中
                invocableMethod.invokeAndHandle(webRequest, mavContainer);
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return null;
                }
    
                return getModelAndView(mavContainer, modelFactory, webRequest);
            }
            finally {
                webRequest.requestCompleted();
            }
        }
    

    这个方法省略了很多源码,暂时先不考虑InitBinder和ModelAttribute,只关注最核心部分

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
            setResponseStatus(webRequest);
    
            if (returnValue == null) {
                if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                    disableContentCachingIfNecessary(webRequest);
                    mavContainer.setRequestHandled(true);
                    return;
                }
            }
            else if (StringUtils.hasText(getResponseStatusReason())) {
                mavContainer.setRequestHandled(true);
                return;
            }
    
            mavContainer.setRequestHandled(false);
            Assert.state(this.returnValueHandlers != null, "No return value handlers");
            try {
                this.returnValueHandlers.handleReturnValue(
                        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
            }
            catch (Exception ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace(formatErrorForReturnValue(returnValue), ex);
                }
                throw ex;
            }
        }
    

    invokeForRequest会根据argumentResolvers解析http的请求输入流,转成Controller方法需要的参数列表,然后根据拿到的参数执行方法,返回结果

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    //获取参数列表
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        return doInvoke(args);
    }
    
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    //根据HandlerMethod拿到对应的参数列表,若为空,则返回
        MethodParameter[] parameters = getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }
        Object[] args = new Object[parameters.length];
    //循环parameters,获取每个参数值
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
    //若argumentResolvers没有一个能支持解析当前parameter,便会抛异常
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
    //若支持了,再根据解析器进行参数值的解析并获取
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
            
            }
        }
        return args;
    }
    

    先来看下判断解析器是否能解析当前参数的逻辑

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
            HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
            if (result == null) {
                for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
                    if (resolver.supportsParameter(parameter)) {
                        result = resolver;
                        this.argumentResolverCache.put(parameter, result);
                        break;
                    }
                }
            }
            return result;
        }
    

    会先从缓存获取,缓存不为null,直接返回,为空,再循环每个resolver,拿到支持解析的resolver后加入缓存便返回
    上面这个例子中,参数是用@RequestBody注解的,因此最终会有这个解析器RequestResponseBodyMethodProcessor,判断的逻辑也很简单,就是看参数是否有@RequestBody注解,因此接下来,看下这个解析器对参数的处理过程

        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
            parameter = parameter.nestedIfOptional();
            Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
            return adaptArgumentIfNecessary(arg, parameter);
        }
    

    很清晰的看出,resolver对参数的解析实际上是通messageConverter来完成的,也就是在HandlerAdapter初始化过程中添加的converter

        protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
                Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    
            MediaType contentType;
            boolean noContentType = false;
            try {
                contentType = inputMessage.getHeaders().getContentType();
            }
            catch (InvalidMediaTypeException ex) {
                throw new HttpMediaTypeNotSupportedException(ex.getMessage());
            }
            Class<?> contextClass = parameter.getContainingClass();
            Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
            HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
            Object body = NO_VALUE;
    
            EmptyBodyCheckingHttpInputMessage message;
            try {
                message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
    
                for (HttpMessageConverter<?> converter : this.messageConverters) {
                    Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                    GenericHttpMessageConverter<?> genericConverter =
                            (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                    if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                            (targetClass != null && converter.canRead(targetClass, contentType))) {
                        if (message.hasBody()) {
                            HttpInputMessage msgToUse =
                                    getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                            body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                    ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                            body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                        }
                        else {
                            body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                        }
                        break;
                    }
                }
            }
            catch (IOException ex) {
                throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
            }
    
            if (body == NO_VALUE) {
                if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                        (noContentType && !message.hasBody())) {
                    return null;
                }
                throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
            }
            return body;
        }
    

    这个方法的执行步骤

    1. 获取messageConverter,执行canRead方法,判断是否可以从http请求读取数据
    2. 执行注解了@ControllerAdvice的RequestBodyAdvice类的beforeBodyRead方法
    3. 执行messageConverter的read方法,获取请求参数对象
    4. 执行注解了@ControllerAdvice的RequestBodyAdvice类的afterBodyRead方法

    看到这,便可知道,若程序要在请求数据达到controller前做一些处理,可以通过三个地方来实现,也就是上述的2、3、4个步骤。这个在一些特殊场景是很有用的,比如前端数据要做一些加密,然后再后端controller接收到时希望是明文的,便可这样处理。

    当参数解析器根据http请求解析出参数对象后,执行controller的方法,获取到结果,会执行returnValueHandler的handleReturnValue,需要将结果写入到http的输出流当中

    this.returnValueHandlers.handleReturnValue(
                        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    
        private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
            boolean isAsyncValue = isAsyncReturnValue(value, returnType);
            for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
                if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
                    continue;
                }
                if (handler.supportsReturnType(returnType)) {
                    return handler;
                }
            }
            return null;
        }
    

    最终会找到这个RequestResponseBodyMethodProcessor,内部也是通过messageConverter将数据写入到http输出流的,根据方法,会看到这个逻辑

    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                    (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ?
                    ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                        (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                        inputMessage, outputMessage);
                if (body != null) {
                    Object theBody = body;
                    addContentDispositionHeader(inputMessage, outputMessage);
                    if (genericConverter != null) {
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    }
                    else {
                        ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                    }
                }
                else {
                }
                return;
            }
        }
    }
    

    这里重点关注下这几个步骤

    1. 根据messageConverter,判断canWrite方法,若返回true
    2. 执行注解了@ControllerAdvice的ResponseBodyAdvice的beforeBodyWrite方法
    3. 执行messageConverter的write方法

    这里也需要注意的一个是,程序可以通过在beforeBodyWrite方法内对返回对象做一些处理,修改数据,然后再写入到http输出流当中。

    总结,HandlerAdapter最主要的三个属性值,argumentResolver,messageConverter,returnValueHandler,argumentResolver内部使用messageConverter将http输入流数据转成controller对应的参数对象,然后执行controller的方法,拿到controller方法返回的结果后,returnValueHandler内部使用messageConverter将controller返回的结果写入到http输出流中

    还有个要注意的地方便是可以通过RequestBodyAdvice和ResponseBodyAdvice对请求和响应做一些特殊处理。

    相关文章

      网友评论

          本文标题:springmvc源码分析-HandlerAdapter原理

          本文链接:https://www.haomeiwen.com/subject/wccnbktx.html