博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC源码解析(三)——HandlerAdapter
阅读量:7074 次
发布时间:2019-06-28

本文共 19715 字,大约阅读时间需要 65 分钟。

hot3.png

前言

    上篇讲的是,HandlerMapping 初始化过程中,是如何将 Handler 与 请求的 url 建立起映射的,我们可以假想一下,“ http://localhost/test ” 的请求过来了,通过映射关系我们找到了 Handler,但这个 Handler 具体是什么类型呢,是基于 @RequestMapping 注解的?还是实现了接口 org.springframework.web.servlet.mvc.Controller 的?不同的实现,处理方式也不一样

    这篇讲的 HandlerAdapter 就是对 Handler 的一个适配,来看源码。

 

源码解读

    首先来看下接口定义。

public interface HandlerAdapter {    // 支持适配的类型    boolean supports(Object handler);    // 调用具体的处理逻辑    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;    // 获取 Header:Last-Modified,用于页面缓存    long getLastModified(HttpServletRequest request, Object handler);}

    下面会重点分析下 support handle 方法。前者是逻辑比较简单,将 Handler 实例传入,具体实现类来判断是否支持适配。而后者涉及到的处理就相对麻烦一些。

    来此之前,依然先回顾下 HandlerAdapter 的初始化过程。

public class DispatcherServlet extends FrameworkServlet {        // 存放加载的 HandlerAdapter实现    private List
handlerAdapters; private boolean detectAllHandlerAdapters = true; // 衔接第一节 mvc初始化 @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { ...... // 关注该方法:HandlerAdapter的初始化 initHandlerAdapters(context); ...... } private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; // 默认探测容器内所有的 HandlerAdapter类型 if (this.detectAllHandlerAdapters) { // beansOfTypeIncludingAncestors:通过 getBeansOfType获取子容器和父容器内的 HandlerAdapter // getBeansOfType会首先调用 getBeanNamesForType获取指定类型的所有 beanName // 然后遍历这些 beanName,使用 getBean创建实例 Map
matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList
(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { try { // 找 beanName为 handlerAdapter的 HandlerAdapter HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { } } if (this.handlerAdapters == null) { // 默认策略:见 DispatcherServlet.properties this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isDebugEnabled()) { .....// 省略日志 } } }}

    跟“  ”相同的处理方式,这里不做赘述。那么接下来,就看看 HandlerAdapter 到底有多少种实现。

 

HttpRequestHandlerAdapter

public class HttpRequestHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {        return (handler instanceof HttpRequestHandler);    }    @Override    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        ((HttpRequestHandler) handler).handleRequest(request, response);        return null;    }}

    支持 HttpRequestHandler 类型的处理器,handle 方法实现也只是简单的向下转型,然后调用接口方法。从源码也能猜测出,如果要使用这样的处理器,只需要实现 HttpRequestHandler.handleRequest 即可。

 

SimpleControllerHandlerAdapter

public class SimpleControllerHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {        return (handler instanceof Controller);    }    @Override    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return ((Controller) handler).handleRequest(request, response);    }}

    对 Controller 类型的支持。

 

SimpleServletHandlerAdapter

public class SimpleServletHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {        return (handler instanceof Servlet);    }    @Override    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        ((Servlet) handler).service(request, response);        return null;    }}

    对 Servlet 类型的支持。

 

AnnotationMethodHandlerAdapter

@Deprecatedpublic class AnnotationMethodHandlerAdapter extends WebContentGenerator        implements HandlerAdapter, Ordered, BeanFactoryAware {    @Override    public boolean supports(Object handler) {        // Handler 下的方法有被 @RequestMapping标识的就返回 true        return getMethodResolver(handler).hasHandlerMethods();    }}

    该类就是 Spring 3.2 版本以前,对于注解形式 Handler 的适配器。它的 handle 方法就不像前面提及的,只是简单做了转型而调用方法就行了。因为还要涉及到很多其他 mvc 注解的解析工作,以及响应信息的处理。

    但它带来的优势是,没有像接口那样,限制了一个 Handler 只能实现对应的一个方法,入参以及返回类型都被接口定义,而不能自定义。由此看来,我们所见灵活性其实是基于框架层面复杂逻辑的封装处理。

    由于这个类已被声明废弃,接替它的是 RequestMappingHandlerAdapter ,所以我们直接来看最新的实现。 

 

RequestMappingHandlerAdapter

public abstract class AbstractHandlerMethodAdapter         extends WebContentGenerator implements HandlerAdapter, Ordered {    @Override    public final boolean supports(Object handler) {        // supportsInternal总返回 true,所以只要满足 Handler为 HandlerMethod即可        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));    }    @Override    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return handleInternal(request, response, (HandlerMethod) handler);    }    protected abstract ModelAndView handleInternal(HttpServletRequest request,                        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;}

    从其抽象父类实现来看,支持的类型为 HandlerMethod,是不是很眼熟,就是我们上篇 “  ”时创建的。具体的 handle 调用了抽象方法 handleInternal,实现委托给了子类。

// RequestMappingHandlerAdapter实现    @Override    protected ModelAndView handleInternal(HttpServletRequest request,                    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ModelAndView mav;        // 如果有必要:对请求 method、以及 session进行校验        checkRequest(request);        // 会话级别的同步:默认为 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 {                mav = invokeHandlerMethod(request, response, handlerMethod);            }        } else {            mav = invokeHandlerMethod(request, response, handlerMethod);        }        /**         * 如果响应头里不包含 “Cache-Control”处理         * - private/must-revalidate:仅在首次访问时访问服务器         * - no-cache:每次访问都会访问服务器         * - max-age:过期之前不会访问服务器         */        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {                /**                 * 该类被 @SessionAttributes标识并指定了 names 或 types                 * 根据 cacheSecondsForSessionAttributeHandlers大小不同来给头赋值                 * HTTP 1.0使用的是 Expires头,HTTP 1.1使用的是 Cache-Control                 */                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);            } else {                // 未被 @SessionAttributes标识类,请求缓存处理                prepareResponse(response);            }        }        return mav;    }

    这个方法主要调用了 invokeHandlerMethod 来调用方法来获取结果,然后就是缓存相关 Header 信息的设置。

    需要注意的是,HTTP 1.0 版本,是通过 “expired” 来记录缓存时间的,它记录的是一个时间戳,在时间戳到期之前,都不会去请求服务端,但时间戳为服务端返回的,并不是客户端生成的,因此存在误差。

    HTTP 1.1 版本,通过 “Cache-Control” 来记录过期时长的,使用 max-age = 秒数,来表示资源在客户端缓存的时长。使用 no-cache 表示不缓存。

// ServletInvocableHandlerMethod实现    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ServletWebRequest webRequest = new ServletWebRequest(request, response);        try {            // 根据类中被 @InitBinder标识的方法,创建一个 WebDataBinder工厂            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);            // 根据类中被 @ModelAttribute标识的方法,创建一个 Model工厂            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);            // 这里传入的 handlerMethod就是 HandlerMapping初始化时创建的            // 至于如何找到请求 url对应的 HandlerMethod,之后的章节会讲解            // 继承体系:ServletInvocableHandlerMethod——>InvocableHandlerMethod——>HandlerMethod            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);            // 赋值操作,用于请求的解析和相应的处理            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);            invocableMethod.setDataBinderFactory(binderFactory);            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);            // 用于记录请求解析和相应            ModelAndViewContainer mavContainer = new ModelAndViewContainer();            // FlashMap:重定向保存上次请求的参数            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));            // 调用被 @ModelAttribute标识的方法,填充 mavContainer            // 将 @SessionAttributes指定的 Session中的属性,填充 mavContainer            // 将 @ModelAttribute标识的参数,如果 @SessionAttributes中存在,同样填充 mavContainer            modelFactory.initModel(webRequest, mavContainer, invocableMethod);            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);            // 创建一个异步请求            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);            // 通过调用 startCallableProcessing启动并发请求处理            // 直到请求处理完前,hasConcurrentResult返回的都是 false            // 初始值为:RESULT_NONE,所以判断 (this.concurrentResult != RESULT_NONE)为 false            if (asyncManager.hasConcurrentResult()) {                Object result = asyncManager.getConcurrentResult();                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];                // 已经获取异步结果,所以要清除维护的结果                asyncManager.clearConcurrentResult();                if (logger.isDebugEnabled()) {                    logger.debug("Found concurrent result value [" + result + "]");                }                // 创建一个嵌套的 ServletInvocableHandlerMethod子类,返回给定的值                // 而不是实际调用控制器方法。 在处理异步返回值时,这非常有用                invocableMethod = invocableMethod.wrapConcurrentResult(result);            }            // 调用对应方法,并使用 HandlerMethodReturnValueHandler处理返回值            invocableMethod.invokeAndHandle(webRequest, mavContainer);            if (asyncManager.isConcurrentHandlingStarted()) {                return null;            }            // mavContainer存储了响应的 viewName、Model数据、Http状态码等信息            // 根据这些信息创建 ModelAndView返回            // 如果是重定向,还会转存 FlashMap数据            return getModelAndView(mavContainer, modelFactory, webRequest);        } finally {            webRequest.requestCompleted();        }    }

    方法的最开始,有对 @InitBinder@ModelAttribute 标识的方法进行收集并创建一个工厂,因为这些方法将作用于 Handler 中所有的方法。

    之后就用上篇  时创建的 HandlerMethod 来创建一个 ServletInvocableHandlerMethod,因为下面的赋值方法是该子类特有的。

    ModelAndViewContainer,这个类就像一个承载请求和相应的容器,请求前将放置一些参数(重定向前参数、@ModelAttribute注解方法返回等等),这些会在调用 invokeAndHandle 时传入,以便处理时需要。

    接下来来看 invokeAndHandle 的实现。

// ServletInvocableHandlerMethod实现    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,                                Object... providedArgs) throws Exception {        // 调用方法并得到返回结果        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);        // 根据 @ResponseStatus注解来设置相应状态        setResponseStatus(webRequest);        if (returnValue == null) {            // 没有返回值            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {                mavContainer.setRequestHandled(true);                return;            }        }        // 如果有返回值,且被 @ResponseStatus指定了 reason        else if (StringUtils.hasText(getResponseStatusReason())) {            mavContainer.setRequestHandled(true);            return;        }        // 设置本次请求是否已被完全处理,通常都是 false        // 例如:@ResponseBody标识的返回需要额外处理        mavContainer.setRequestHandled(false);        try {            // 挑选一个 HandlerMethodReturnValueHandler处理返回值(策略)            // 例如:RequestResponseBodyMethodProcessor.handleReturnValue(源码在下方)            this.returnValueHandlers.handleReturnValue(                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);        } catch (Exception ex) {            if (logger.isTraceEnabled()) {                logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);            }            throw ex;        }    }

    这里的 invokeForRequest 方法下边我们会讲,其实就是通过一次反射调用,来获取到方法的返回值(例如,UserController.select 返回了查询到的 User 对象)。

    当然,也有 void 无返回值方法,那么判断请求是否处于缓存期内、或指定了 @ResponseStatus.code 属性,就直接返回了。

    如果有返回值,但指定了 @ResponseStatus.reason 属性,那也直接返回了。

    Ps:关于 @ResponseStatus,它一般和 @ExceptionHandler 结合使用,用于异常的统一处理,以及响应码、提示信息的统一返回。

    否则就要通过 HandlerMethodReturnValueHandler 来对响应结果进行处理。

// InvocableHandlerMethod实现    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,                                   Object... providedArgs) throws Exception {        // 根据请求解析出方法入参        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);        if (logger.isTraceEnabled()) {            logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +                    "' with arguments " + Arrays.toString(args));        }        // 通过反射调用方法        Object returnValue = doInvoke(args);        if (logger.isTraceEnabled()) {            logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +                    "] returned [" + returnValue + "]");        }        return returnValue;    }

    可以看到 invokeForRequest 方法逻辑很简单,第一步调用方法 getMethodArgumentValues 对入参进行解析,第二步直接通过反射调用相应方法拿到响应结果。所以难点在参数的解析上。

// InvocableHandlerMethod实现    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,                                             Object... providedArgs) throws Exception {        // 获取调用方法的参数        MethodParameter[] parameters = getMethodParameters();        Object[] args = new Object[parameters.length];        for (int i = 0; i < parameters.length; i++) {            MethodParameter parameter = parameters[i];            // ParameterNameDiscovery:用于获取方法入参名称            // - LocalVariableTableParameterNameDiscoverer:根据 class文件中的 LocalVariableTable解析            // - AspectJAnnotationParameterNameDiscoverer:根据 aspectj注解的 argNames            // - StandardReflectionParameterNameDiscoverer:基于反射获取            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);            // 根据入参提供的 providedArgs,找出符合类型的            // 根据上面代码追溯,入参 providedArgs为 null,因此直接返回 null            args[i] = resolveProvidedArgument(parameter, providedArgs);            if (args[i] != null) {                continue;            }            // argumentResolvers类型为:HandlerMethodArgumentResolverComposite            // 组合模式将所有 HandlerMethodArgumentResolver实现包装            // 例如:RequestResponseBodyMethodProcessor,支持 @RequestBody、@ResponseBody            if (this.argumentResolvers.supportsParameter(parameter)) {                try {                    // 调用实现方法对参数进行解析                    args[i] = this.argumentResolvers.resolveArgument(                            parameter, mavContainer, request, this.dataBinderFactory);                    continue;                } catch (Exception ex) {                    if (logger.isDebugEnabled()) {                        ....// 省略日志                    }                    throw ex;                }            }            if (args[i] == null) {                ....// 未解析出的参数会抛异常 IllegalStateException            }        }        return args;    }

    Spring 会将方法的入参相关信息封装成 MethodParameter 对象,具体的解析逻辑交给了不同的 HandlerMethodArgumentResolver 实现类,比如我们常使用的 Json 请求响应,我们来看 RequestResponseBodyMethodProcessor 是如何解析的。

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {    @Override    public boolean supportsParameter(MethodParameter parameter) {        // 支持解析参数的条件:被 @RequestBody标识        return parameter.hasParameterAnnotation(RequestBody.class);    }    @Override    public boolean supportsReturnType(MethodParameter returnType) {        // 支持处理返回的条件:返回方法或类型被 @ResponseBody标识        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||                returnType.hasMethodAnnotation(ResponseBody.class));    }    @Override    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {        // 如果是参数类型 Optional,返回一个嵌套级别的 MethodParameter        parameter = parameter.nestedIfOptional();        // 调用 HttpMessageConverter解析请求参数        // 根据 contentType选择不同的转换器处理,比如 application/json,可以使用 MappingJackson2HttpMessageConverter        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());        // 获取参数(包括数组和集合)常规名称:见 ClassUtils.getShortNameAsProperty        String name = Conventions.getVariableNameForParameter(parameter);        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);        if (arg != null) {            // 对 @Validated或 @Valid的校验            // 实现:调用 validate.validate            validateIfApplicable(binder, parameter);            // 如果有校验未通过的,抛出 MethodArgumentNotValidException            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());            }        }        mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());        // 对于入参类型为 Optional的,要封装一下        return adaptArgumentIfNecessary(arg, parameter);    }    @Override    public void handleReturnValue(Object returnValue, MethodParameter returnType,                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest)            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {        mavContainer.setRequestHandled(true);        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);        // 调用 HttpMessageConverter处理响应结果        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);    }}

    支持的入参和返回,从方法可以很容易看出,就是支持 body 体数据解析的 @RequestBody,和支持 body 体数据响应的 @ResponseBody。但具体支持的是 Json 格式还是 Xml 格式,是由 HttpMessageConverter 来支持的。

    这里还有一点就是对 @Validate@Valid 标识入参的校验,如果校验不通过,会以 MethodArgumentNotValidException 异常抛出,具体的提示信息(例如 @NotNull( message="不能为空" ))也会封装在内。

    到这里,一次请求的大致流程也基本解读完毕。涉及到一些具体的像“Json数据解析过程”,“响应数据的处理”本篇没有展开讲解。

 

总结

    这一节主要的内容就是 Handler 的适配以及调用逻辑,这些逻辑将作为后续章节介绍一次具体请求流程的铺垫。

转载于:https://my.oschina.net/marvelcode/blog/1838329

你可能感兴趣的文章
java对文件的检索
查看>>
Marquee滚动字幕设置(转)
查看>>
linux系统下调度数据库类型资源库中的kettle job
查看>>
8UFTP
查看>>
VC 2005 解决方案的目录结构设置和管理
查看>>
吾爱论坛浏览器分享
查看>>
java内存模型优化建议
查看>>
解决Ubuntu Kylin 1610安装ANSYS17.2的NVIDIA显卡驱动问题
查看>>
Linux下如何修改Apache根目录
查看>>
JAVA入门[2]-安装Maven
查看>>
什么是回调函数
查看>>
HDU 2588 GCD &amp;&amp; GCD问题总结
查看>>
2015年北京大学软件project学科优秀大学生夏令营上机考试---C:单词翻转面试题...
查看>>
cocos2d-x 3.0的坑有哪些
查看>>
awk条件语句
查看>>
TCP端口状态说明ESTABLISHED、TIME_WAIT
查看>>
I.MX6 android 4.2 源码下载
查看>>
md5sum 生成 经md5加密后的字符串
查看>>
PowerShell应用之-批量执行SQL脚本
查看>>
职场加薪步步高升的五大法则
查看>>