为了账号安全,请及时绑定邮箱和手机立即绑定

Spring MVC返回视图原理(五)

标签:
Java

Spring MVC非常灵活,在使用的时候可以返回视图,也可以直接返回普通数据,在想,内部是怎么实现的呢?

经过了几天研究Spring MVC的源码,可以看前几篇文章,今天再弄明白下为什么有时候返回视图,有时候直接返回数据呢。

分析

  1. 首先配置web.xml并且准备好视图,做一些准备工作
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

dispatcher-servlet.xml中视图配置


    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

Controller类

    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "hello";
    }

    @RequestMapping("/index")
    public String indexView(){
        return "index";
    }

image.png

  1. 访问进行调试,关于DispatcherServlet的流程,我们前面的文章已经都说过了,这里不做太多的赘述,getHandler, getHandlerAdapter,基本不变,在HandlerAdapter.handler(Handler)的时候内部有些不同。

    返回视图与返回数据刚开始步骤基本都一样,invoke返回的值也是相同的。

    真正产生变化的地方,在处理最后的返回值时

    image.png

  2. 处理返回值,在激发方法之后,要决定要用什么返回值处理器来处理返回的类型。其中涉及到一个接口HandlerMethodReturnValueHandler,真正用来处理的。

HandlerMethodReturnValueHandler有两个方法:

  • supportsReturnType是否支持当前的返回值类型,如果支持的话使用当前的HandlerMethodReturnValueHandler处理
  • handleReturnValue,处理返回值
/**
     * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
     * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
     */
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        }
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }

    private HandlerMethodReturnValueHandler selectHandler(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;
    }

image.png

如果是@ResponseBody注解的时候由RequestResponseBodyMethodProcessor处理。

//RequestResponseBodyMethodProcessor
@Override
    public boolean supportsReturnType(MethodParameter returnType) {
       //判断是否有ResponseBody.class的注解
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }

返回值是String,但不加@ResponseBody注解的时候由ViewNameMethodReturnValueHandler处理。

  // ViewNameMethodReturnValueHandler
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
       //参数类型如果是void或者可以CharSequence.class.isAssignableFrom
        Class<?> paramType = returnType.getParameterType();
        return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
    }

如果返回值为ModelAndView的时候,由ModelAndViewMethodReturnValueHandler处理


    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
    }

其余的可以自行查看。

  1. 上一步因为不同的ReturnValueHandler处理returnValue的结果不一样,Spring可以进行下一步的操作。

对于返回数据类型的操作,其将mavContainer的requestHandled设置为true,然后ModelAndView就会返回为null,后续的处理就不按照视图来处理。

如果返回的是视图,那么requestHandled仍然为false,后续再processDispatchResult中进行render.

  1. 后续和之前提到的DispatcherServlet都一样了。

回顾

Spring MVC对不同的返回值处理的方式不同,依靠的是HandlerMethodReturnValueHandler的不同实现。

主要在invoke方法之后,返回值与刚才激发的方法都被封装在ServletInvocableHandlerMethod里面了,其可以通过Method对象获取加在方法上的注解,类等等信息,最后做出结果。

最后

抓住DispatcherServlet的大体框架之后,其中的细节就简答的多了。

点击查看更多内容
5人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1.1万
获赞与收藏
1544

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消