细解spring mvc架构

概述

网上关于spring mvc的架构解析类的文章已经很多了,可以说是多如牛毛,但是为什么这里还要补充这么一篇呢?

这一篇实际上也没有太多的新内容,但是也包含一些新内容。描述的角度上也稍微有一些区别,而且关注了一点其他文章没有关注的问题点和新内容,如果有时间不妨一看。

以下内容,是在做着调研是否可以自定义一个MessageConverter,实现@ResponseBody结果依据请求路径不同,解析成两种不同json格式的可能性时的调研总结。

这里的描述适用于spring mvc 3.0 -4.0版本。

spring mvc整体流程

整体流程都是一样的,如果看到的不一样,那么很可能是错的,也有可能是将来很远的spring mvc版本。下图是spring请求处理流程图(摘自开涛博客):

这里写图片描述

这张图已经标注的比较明确。接下来,我们从实际使用的角度,来对照spring 工程中各个部分的xml配置进行进一步的对照说明。

Dispatcher

Dispatcher是前端控制器,是整个spring mvc框架的对外接口点,和全局调度器。request和response都从这里经过,请求的处理流程由Dispatcher控制。Dispatcher是一个Servlet的实现类,需要配置在web.xml里面,或者添加到WebApplicationInitializer(全注解方式配置)里面。

Xml配置方式:
这里写图片描述

注解配置方式:

这里写图片描述

Dispatcher中的主要代码:

这里写图片描述

第一个红框是HandlerMapping对应的处理位置,第二个红框是HandlerAdapter对应的处理位置,第三个红框是ViewResolver对应的处理位置。

HandlerMapping

HandlerMapping的作用是通过请求的url找到对应的Controller中的处理方法,也就是映射。然而,你会发现这个类我们没有在任何地方配置过,他是怎么就加载到Spring容器里边的呢?又加载的是哪一个呢?

Spring 默认提供了这些HandlerMapping的实现类:

这里写图片描述

这里写图片描述

当DispatcherServlet初始化的时候,就会把所有能在ApplicationContext里面找到的HandlerMapping实例(xml或者注解方式添加到spring 容器中)添加到自己的handlerMappings集合里面,这样就可以在后续的doDispatch中使用这些HandlerMapping了。

以下是DispatcherServlet初始化handlerMappings集合的位置:
这里写图片描述

那么,这些HandlerMapping是在哪里被标记为bean了呢,或者是是什么时候以bean的形式被添加到ApplicationContext里边了呢?关键就在于<mvc:annotation-driven />, 这个配置项会自动注入mvc需要的HandlerMapping和HandlerAdapter,所以一旦添加了这个配置,我们就不用配置HandlerMapping和HandlerAdapter了。注意,annotation-driven还给我们注入了其他mvc需要的东西。

HandlerMapping解析后返回的是一个HandlerMethod实例,其中包含了处理请求的类和方法:

这里写图片描述

HandlerAdapter

HandlerAdapter是spring mvc的主要执行器,像参数解析、方法调用、结果数据封装都是在HandlerAdapter中触发的。

Spring里面有两个默认实现的HandlerAdapter,一个是AnnotationMethodHandlerAdapter,另一个是RequestMappingHandlerAdapter。在spring3和spring3之前一般用的是AnnotationMethodHandlerAdapter,从spring 3.1开始慢慢的转变成使用RequestMappingHandlerAdapter。

HandlerAdapter中的主要代码逻辑:
这里写图片描述

invokeAndHandle完成了参数提取和解析、方法调用、响应数据解析(包含@ResponseBody数据解析)。

invokeAndHandle方法的核心代码:

这里写图片描述

其中invokeForRequest完成参数解析、方法调用。handleReturnValue完成返回结果数据的整理和处理,@ResponseBody数据的处理也是由handleReturnValue完成。

大家都知道@ResponseBody标注的方法返回结果会被解析成json串,而不会进行模板视图的渲染,那么spring是怎么控制跳过模板视图渲染这个步骤的呢?注意上图的第二个红框setRequestHandled,当handlReturnValue处理过程中遇到@ResponseBody注解的方法时,处理过程中会把requestHandled设置为true,后续的视图解析过程就会跳过,不进行模板渲染。

这里写图片描述

也就是说@ResponseBody注解方法的返回结果数据是直接在HandlerAdapter里面处理的,处理之后把ModelAndView设置为null,从而后续的视图渲染环节就不进行处理了。

从上边的说明,我们知道@ResponseBody注解方法的处理是在HandlerAdapter里面由returnValueHandlers处理的,一般我们也没有配置过returnValueHandlers属性,他们是怎么来的呢?

HandlerAdapter初始化时会自动创建一组默认的returnValueHandlers,代码位置如下:

这里写图片描述

既然,@ResponseBody注解方法返回数据的处理类是默认创建的,那么还能不能自定义呢?按照spring几个初始化点的加载顺序来说,是可以的。Spring几个初始化点的加载顺序是这样的:构造器-->自动注入-->PostConstrut-->InitializingBean-->xml中配置init方法

上图中的getDefaultReturnValueHandler方法是通过InitializingBean的方式调用的,也就是在它之后还有一个xml中配置的init方法,也就是说我们还有一个xml中配置init的方式来覆盖掉默认创建的处理器。

由于RequestMappingHandlerAdapter调用getDefaultReturnValueHandler获取默认的returnValueHandler之前有一个判断,判断returnValueHandlers是否为空,如果不为空,那么就不调用getDefaultReturnValueHandler。所以我们还可以在InitializingBean之前的位置注入自己的returnValueHandlers。

这里写图片描述

ViewResolver

ViewResolver的作用是通过请求信息、返回结果和方法映射信息获取View对象,View对象包含一个renderMergedTemplateModel方法,调用后会渲染结果数据为对应的展示形式(页面、xml、csv等等)。

ViewResolver可以单独配置成一个特定的类,这样只能处理一种类型的视图解析。如果有需要设置多种类型的视图解析,可以使用ContentNegotiatingViewResolver配置多种视图解析器。ContentNegotiatingViewResolver可以依据请求参数中的format值、url的尾缀、请求头中的MediaType三种方式确定返回结果类型。

单视图解析器的话,我们可以直接配置一种就可以了,比如使用FreeMarker模板引擎,配置如下:

<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="false" />
        <property name="prefix" value="" />
        <property name="suffix" value=".ftl" />
        <property name="contentType" value="text/html;charset=UTF-8"></property>
        <property name="requestContextAttribute" value="rc" />
        <property name="exposeSpringMacroHelpers" value="true" />
        <property name="exposeRequestAttributes" value="true" />
        <property name="exposeSessionAttributes" value="true" />
        <property name="allowSessionOverride" value="true" />
    </bean>

使用ContentNegotiatingViewResolver配置如下:

<!-- ViewResolver -->  
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">  
        <property name="order" value="1" />  
        <property name="favorParameter" value="false" />  
        <property name="ignoreAcceptHeader" value="true" />  

        <property name="mediaTypes">  
            <map>  
                <entry key="json" value="application/json" />  
                <entry key="xml" value="application/xml" />  
            </map>  
        </property>  

        <property name="defaultViews">  
            <list>  
                <!-- JSON View -->  
                <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />  
                <!-- JAXB XML View -->  
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">  
                    <constructor-arg>  
                        <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">  
                            <property name="classesToBeBound">  
                                <list>  
                                    <value>learning.webapp.model.EmployeeX</value>  
                                </list>  
                            </property>  
                        </bean>  
                    </constructor-arg>  
                </bean>  
            </list>  
        </property>  
    </bean>  

    <!-- If no extension matched, use JSP view -->  
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
        <property name="order" value="2" />  
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />  
        <property name="prefix" value="/WEB-INF/views/jsp/" />  
        <property name="suffix" value=".jsp" />  
    </bean>  

如果想自定义视图解析器,可以参见作者之前写的一篇《自定义springmvc视图解析器》

总结一下

在配置spring mvc的时候,我们只需要配置Dispatcher、ViewResolver,xml中添加annotation-driven就可以了。

如果是放到spring 3.0之前,没有annotation-driven的时候,我们需要自己配置HandlerMapping和HandlerAdapter,同时也不能支持@ResponseBody注解的方法。

所以,网上很多的配置教程都是混杂了spring 2和spring 3的,有些配置项完全可以省掉。

Annotation-driven & Component-scan & annotation-config

Component-scan: 把以下注解的类实例化到容器中:@Component、@Repository、@Service、@Controller;解析自动注入的注解: @Autowired、@Qualifier。

annotation-config:处理已经包含到容器(注解或者xml配置方式实例化的bean)中实例的@Autowired、@Qualifier注解。

Annotation-driven:会实例化spring mvc相关的一些必要对象,比如HandlerMapping和HandlerAdapter。由于spring mcv有两个容器,annotation-driven只能应该,且必须放在Dispatcher对应的配置文件里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值