SpringMVC4.x 笔记(2):Spring MVC中MVC设计

请求流程

请求流程图

Spring MVC 的核心类DispatcherServlet在初始化完成后,就可以接收HTTP请求。其继承了HttpServlet基础类,覆盖了service()方法。在该方法被调用时,DispatcherServlet 开始执行接收用户请求并返回请求响应的工作。简单请求执行过程如下:
在这里插入图片描述

流程分析

  1. 映射DispatcherServlet阶段。客户端发出一个HTTP请求,Web应用服务器接收到这个请求,如果匹配DispatcherServlet的请求映射路径servlet-mapping,则将该请求交给该DispatcherServlet处理

  2. 属性参数准备阶段。DispatcherServlet 接收到这个请求后,处理本地化解析,往request对象中设置了一堆属性,使框架对象对处理程序和视图对象可用。

  3. 映射处理器阶段。接收到请求后,将根据请求的信息(包括URL、HTTP方法、请求报文头、请求参数等)以及HandlerMapping的配置找到处理请求的处理器Handler

  • 这是HandlerMapping完成的工作,该接口规定了一个URL必须返回一个HandlerExecutionChain处理链,可以在这个处理链中添加任意的处理器、拦截器来处理这个URL对应的请求
  1. 处理器Handler执行阶段。找到Handler以后,通过HandlerAdapterHandler进行封装,再以统一的适配器接口调用处理器Handler方法。该阶段的做了大量的工作,包括请求参数解析、数据绑定方法的调用、数据验证、数据转换、业务方法调用、返回值解析等。

  2. 处理器完成业务逻辑处理后,返回一个ModelAndView对象,其包含了视图逻辑名和模型数据信息。

  3. 解析视图阶段。ModelAndView中没有真正的视图对象,DispatcherServlet 借由视图解析器ViewResolver完成逻辑视图名到真正的视图对象的解析工作。并使用这个视图对象View对象对模型数据进行视图渲染。

  4. 返回响应阶段。最终客户端得到响应消息,这个消息可能是一个HTML页面、也可能是XML或者JSON数据、或者是图片、PDF等媒体数据

通过以上的简单流程,可以清楚的看到框架对于Control、Model、View(MVC)设计中涉及的组件,下面具体分析来看。

Control 设计

  1. Spring MVC 的Control功能主要是由HandlerMappingHandlerAdapter两个组件提供的。

  2. HandlerMapping负责映射用户的URL和对应的处理器Handler,即按什么规则去寻找需要访问的ControllerHandlerMapping并没有规定如何映射,只规定了一个URL必须返回一个HandlerExecutionChain处理链,可以在这个处理链中添加任意的处理器、拦截器来处理这个URL对应的请求。

  3. HandlerAdapter负责对各种处理器Handler进行封装,是一个适配器接口。

  • Spring MVC 没有提供具体的Handler接口,任意一个Object都可以是一个处理器。
  • 如果需要把特别的一个URL对应到一个Handler,那么这个Handler必须是复合某种规则的,最常见的办法就是所有的Handler都是实现某个接口,然后框架会直接调用这个接口中定义的特殊方法(或者不用继承某个接口,但是都需要采用固定的方法,反射调用实现)。像Struts1、Struts2、Webx都是类似的实现
  • Spring MVC 中HandlerAdapter的设计给出了另一种扩展方式

处理器映射 HandlerMapping

HandlerMapping 概述
  1. Spring MVC 本身提供了很多HandlerMapping实现,目前版本(4.3.X)主要的实现为BeanNameUrlHandlerMappingSimpleUrlHandlerMappingRequestMappingHandlerMappingEmptyHandlerMapping 四个,其他如DefaultAnnotationHandlerMappingControllerClassNameHandlerMappingControllerBeanNameHandlerMapping等已经废弃。框架支持同时配置多种HandlerMapping实现类型,多种类型之间不会冲突。框架提供的有关HandlerMapping的类图如下所示:
    在这里插入图片描述
  2. HandlerMapping有一个重要的抽象类AbstractHandlerMapping,其同时实现了Ordered接口并继承了WebApplicationObjectSupport类,可以让HandlerMapping通过设置setOrder()提高优先级,并通过覆盖initApplicationContext方法实现初始化的一些工作。
HandlerMapping 初始化

HandlerMapping 初始化完成的两个最重要的工作就是将URL与Handler的对应关系保存起来,并将所有的interceptors对象保存在adaptedInterceptors集合中,等请求到来时执行所有的adaptedInterceptors集合中的interceptors对象,所有的interceptors对象必须实现HandlerInterceptor或者WebRequestInterceptor接口。

  1. 将URL与Handler的对应关系保存起来,不同的HandlerMapping实现类,具体实现不同。
  • AbstractUrlHandlerMapping及其子类将对应关系存储在handlerMap集合中,如BeanNameUrlHandlerMappingSimpleUrlHandlerMapping
  • AbstractHandlerMethodMapping及其子类将对应关系存储在mappingRegistry映射注册器中,如RequestMappingHandlerMapping
  1. 将所有的interceptors包装成成HandlerInterceptor对象,保存在adaptedInterceptors集合中,所有的interceptors对象必须实现HandlerInterceptor或者WebRequestInterceptor接口。

  2. SimpleUrlHandlerMapping为例,其初始化的时序图如下:
    在这里插入图片描述

常用HandlerMapping了解
BeanNameUrlHandlerMapping
  1. 该处理器是默认的处理器。根据请求的URL和容器中定义的处理器Bean的name属性进行匹配,即name属性就是映射的URL地址。把处理器Bean的定义和请求URL进行了绑定,这也是其不足之处。
  2. 根据规定处理器Bean的name属性必须以斜杠/开头,不然无法加入其内部属性urls集合中,也就无法添加到handlerMap
# 注册处理器,name属性需要以斜杠`/`开头,不然不会匹配
<bean id="firstController" name="/hello.do" class="com.springmvc.xml.controller.FirstController">

SimpleUrlHandlerMapping
  1. 简单URL映射,支持映射到bean实例和映射到bean名称(后者要求非单例处理器),通过属性urlMapmappings将URL和处理器的定义分离,开可以对URL进行统一的管理
  2. 属性urlMapmappings的作用是一样的,会将请求的URL与映射器这两个属性子标签的key定义的值进行匹配,匹配上后,再将key对应的value值与处理器Bean定义的id值进行匹配从而找到处理器的Bean。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="mappings">
		<props>
			<prop key="/hello.do">firstController</prop>
			<prop key="/first.do">firstController</prop>
		</props>
	</property>
	
	<!--
	不加斜杠,程序会自动帮你补上
	<property name="urlMap">
		<map>
			<entry key="/hello.do" value="firstController"/>
			<entry key="/first.do" value="firstController"/>
		</map>
	</property>
	-->
</bean> 

<bean id="firstController" class="com.springmvc.xml.controller.FirstController">

RequestMappingHandlerMapping
  1. 上面涉及的两个HandlerMapping都是和Schema配置相关的,日常使用@RequestMapping注解实现需要一个全新的处理器映射,以前的版本为DefaultAnnotationHandlerMapping,现在已经被RequestMappingHandlerMapping代替。

  2. 从上面的HandlerMapping类图可以看到,间接继承了AbstractHandlerMapping类,且进一步封装继承了两个父类AbstractHandlerMethodMappingRequestMappingInfoHandlerMapping,主要定义了请求处理器类为HandlerMethod,请求与处理器的映射关系类为RequestMappingInfo

  3. RequestMappingHandlerMapping实现了InitializingBean生命周期接口,覆盖了初始化方法afterPropertiesSet,其主要功能是扫描带有注解@Controller或者@RequestMapping的Bean,并且扫描其方法,根据@RequestMapping配置的信息,构建RequestMappingInfo对象,注册到MappingRegistry中。该方法的执行顺序在ApplicationContextAware#setApplicationContext后面。具体Bean生命周期见 Spring4.x 笔记(3):Spring 容器中 Bean 的生命周期

  4. RequestMappingHandlerMapping还实现了两个接口MatchableHandlerMappingEmbeddedValueResolverAware。这两个接口分别的作用为:

  • MatchableHandlerMapping的作用是根据requestpattern来进行匹配,即确定给定的请求是否匹配请求条件
  • EmbeddedValueResolverAware,通过该接口可以获取Spring容器加载的properties文件属性值,不同于读取静态文件的方式,这种方式能加载类似于${test.a}/${test.b}的属性值
  1. RequestMappingHandlerMapping的初始化的时序图如下:
    在这里插入图片描述
HandlerMapping 的使用
  1. DispatcherServlet内部组件初始化中,在没有自定义的情况下,框架本身默认配置的是BeanNameUrlHandlerMapping

  2. @EnableWebMvc 注解的使用,扩展了HandlerMapping组件,在WebMvcConfigurationSupport类中定义了:

Bean名称Bean类型顺序备注
requestMappingHandlerMappingRequestMappingHandlerMapping顺序 0最高优先级,最核心的处理器映射
viewControllerHandlerMappingSimpleUrlHandlerMapping顺序 1给简单视图使用,覆盖 WebMvcConfigurer#addViewControllers 方法实现,不需要写控制器类或者方法,处理简单的直接返回
beanNameHandlerMappingBeanNameUrlHandlerMapping顺序 2
resourceHandlerMappingSimpleUrlHandlerMapping顺序 LOWEST_PRECEDENCE - 1静态资源使用,覆盖WebMvcConfigurer#addResourceHandlers方法实现功能
defaultServletHandlerMappingSimpleUrlHandlerMapping顺序 LOWEST_PRECEDENCE最低优先级,覆盖WebMvcConfigurer#configureDefaultServletHandling方法可以实现功能

处理器适配器 HandlerAdapter

HandlerAdapter 概述
  1. HandlerMapping 可以完成URL与Handler的映射关系,但是Spring MVC 没有提供具体的Handler接口,任意一个Object都可以是一个处理器。这就需要HandlerAdapter对处理器进行统一包装。

  2. HandlerAdapter接口提供了三个方法:

方法说明
supports(Object handler)通过Handler对象参数,判断该HandlerAdapter是否支持
handle()处理请求的具体方法,使用给定的处理程序处理此请求。返回ModelAndView对象
getLastModifiedHttpServlet#getLastModified方法相同。如果处理程序类中不支持,则可以简单地返回-1。
  1. 框架提供了很多HandlerAdapter的实现,类图如下:
    在这里插入图片描述

  2. 以下框架提供的典型HandlerAdapter实现类。

类名header 2
SimpleServletHandlerAdapter支持处理器为Servlet的适配器,使用一个Servlet作为处理器
HttpRequestHandlerAdapter支持处理器为HttpRequestHandler的适配器,所有的Handler可以实现handleRequest方法,该方法没有返回值,即最后返回的 ModelAndView 为空
SimpleControllerHandlerAdapter支持处理器为Controller的适配器,所有的Handler可以实现handleRequest方法,该方法会返回ModelAndView对象,用于后续的模板渲染
RequestMappingHandlerAdapter1、支持处理器为HandlerMethod类型的适配器,处理器方法被反射调用。
2、该适配器提供了很多功能组件的属性设置,如参数解析器customArgumentResolvers、消息转换messageConverters
AnnotationMethodHandlerAdapter废弃,被RequestMappingHandlerAdapter代替
  1. Spring MVC使用适配器HandlerAdapter的方式设计处理器Handler,使得Handler更加灵活,不需要和其他框架一样必须实现某个接口或者固定的方法。后续如果需要添加新的Handler,只需要增加一个Handler处理器类(没有类型限制),然后增加一个匹配的HandlerAdapter即可。
HandlerAdapter 初始化
  1. 除了RequestMappingHandlerAdapter,其他的HandlerAdapter没有什么特别的地方。
  2. RequestMappingHandlerAdapter与处理器类型为HandlerMethod相匹配,与该处理器匹配的处理器映射为RequestMappingHandlerMapping,处理器映射关系类为RequestMappingInfo,支持使用@RequestMapping注解实现的处理器。
  3. RequestMappingHandlerAdapter有一个特殊抽象父类AbstractHandlerMethodAdapter。在其初始化过程中主要是定义支持的HTTP方法,默认是不作任何限制。
HandlerAdapter 使用
  1. DispatcherServlet内部组件初始化中,在没有自定义的情况下,框架本身默认配置的是HttpRequestHandlerAdapterSimpleControllerHandlerAdapterAnnotationMethodHandlerAdapter

  2. @EnableWebMvc 注解的使用,扩展了HandlerAdapter组件,在WebMvcConfigurationSupport类中定义了三种类型的Bean:

Bean名称Bean类型备注
requestMappingHandlerAdapterRequestMappingHandlerAdapter
httpRequestHandlerAdapterHttpRequestHandlerAdapter
simpleControllerHandlerAdapterSimpleControllerHandlerAdapter

Control 的调用逻辑

  1. 整个Spring MVC的调用是从DispatcherServletdoService()方法开始的,在doService()方法中会将applicationContextlocaleResolverthemeResolverflashMapManager等对象添加到request中以便在后面使用。接着调用doDispatch()方法,这个方法是主要的处理用户请求的

  2. Control 的处理逻辑关键是根据请求的URL从DispatcherServlet类的处理器映射集合handlerMappings中匹配每个HandlerMapping对象中的某个处理器Handler,匹配成功后返回这个的处理链HandlerExecutionChain对象,这个对象中将会包含用户自定义的多个拦截器HandlerInterceptor对象。

  3. DispatcherServlet会根据Handler对象在其适配器集合属性handlerAdapters中匹配哪个HandlerAdapter实例来支持该Handler对象,然后执行相应的Handler对象的相应方法。

  4. Control 的调用逻辑时序图如下图:
    在这里插入图片描述

Model 设计(TODO)

  1. HandlerAdapter的设计来看,其核心方法handle()会返回一个ModelAndView对象,但是处理器Handler会有没有返回的情况。只有当ModelAndView不为空时,说明处理器需要传一个Model实例给View去渲染模板,如@ResponseBody注解使用时,ModelAndView对象返回就为空,这时就不需要渲染视图模板。

  2. ModelAndView对象是连接业务逻辑层与View展现层的桥梁,是连接处理器Handler与View的桥梁,其包含了ModelMap对象和Object类型的视图对象,可以是视图逻辑名或者视图对象。

  3. ModelMap对象就是执行模板渲染时所需要的变量对应的实例,是LinkedHashMap的子类,在Handler中将模板中需要的对象存在这个Map中,然后传递到View对应的视图解析其ViewResolvers中,如Jsp中,会将每个ModelMap中的元素分别设置到request.setAttribute(modelName, modelValue);中。

View 设计

  1. 在Spring MVC 中,View模块相关的有两个组件,分别是请求到视图名翻译器RequestToViewNameTranslator、视图解析器ViewResolver

  2. RequestToViewNameTranslator支持用户自定义对ViewName的解析,如将请求的ViewName加上前缀或者后缀,或者替换成特定的字符串等。

  3. ViewResolver用于根据用户请求的ViewName创建合适的模板引擎来渲染最终的页面。ViewResolver会根据ViewName创建一个View对象,调用视图对象的View#rend()方法渲染页面

ViewResolver 初始化

  1. RequestToViewNameTranslator 的初始化工作比较简单,只是让Sring 创建的Bean 的对象保存在DispatcherServletviewNameTranslator属性中。这部分工作在内部核心组件初始化时完成。

  2. ViewResolver的初始化过程比RequestToViewNameTranslator复杂一些,ViewResolver接口只有一个方法View resolveViewName(String viewName, Locale locale),负责根据viewName创建View对象。ViewResolver的类图如下:
    在这里插入图片描述

  3. ViewResolver接口有个抽象实现类AbstractCachingViewResolver,这个类定义了一个抽象方法View loadView(String viewName, Locale locale),该方法会根据viewName创建View对象。该类还继承了WebApplicationObjectSupport,所有的子类可以通过覆盖initApplicationContext方法在框架启动的时候完成必要的初始化工作,如VelocityViewResolver

  4. UrlBasedViewResolver类实现了AbstractCachingViewResolver抽象类,通过设置viewClass来创建view对象。如果使用InternalResourceViewResolver类,则需要将viewClass设置为InternalResourceView.class;如果使用FreeMarkerViewResolver类,则需要将viewClass设置为FreeMarkerView.class

  5. JSP的ViewResolver对应的是InternalResourceViewResolver类,InternalResourceView有一个子类JstlView。当调用ViewResolver#resolveViewName方法时会调用AbstractCachingViewResolver#createView方法,将viewClass属性对应的InternalResourceView类实例化。最后调用InternalResourceView#render方法渲染JSP页面

  6. View 执行时序图如下所示:
    在这里插入图片描述

ViewResolver 的使用

  1. DispatcherServlet内部组件初始化中,在没有自定义的情况下,框架本身默认配置的是DefaultRequestToViewNameTranslatorInternalResourceViewResolver

  2. @EnableWebMvc 注解的使用,WebMvcConfigurationSupport类扩展定义了类型为ViewResolverCompositeViewResolver,该类是一个组合类,支持多个视图解析器,如果没有自定义实现默认添加类型为InternalResourceViewResolver的视图解析器。该解析器定义的prefixsuffix都是空值。就应用实现来说,需要在使用的时候自定义prefixsuffix属性值符合应用实际情况。

参考

  1. Spring4.x 笔记(3):Spring 容器中 Bean 的生命周期
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值