Spring MVC工作机制-深入分析Java Web技术内幕

Spring MVC的总体设计

web.xml中需要配置一个DispatcherServlet。

 

Spring MVC如何工作?

(1)、DispatcherServlet的initStrategies初始化方法:

  1. 文件上传服务、国际化、主题相关组件
  2. 请求映射关系(initHandlerMappings)、根据Handler的类型定义不同的处理规则(initHandlerAdapters)
  3. Handler异常处理、ViewName转换、View解析页面相关

这8个组件对应的8个Bean对象都保存在DispatcherServlet类中。

 

(2)、Spring MVC组件图

在Spring MVC框架中,有三个组件是用户必须要定义和扩展的:定义URL映射规则、实现业务逻辑的Handler实例对象、渲染模板资源。

Spring容器的创建是在FrameworkServlet的initServletBean()方法中完成的,这个方法会创建WebApplicationContext对象,并调用其refresh()方法来完成配置文件的加载,配置文件的加载同样是先查找Servlet的init-param参数中设置的路径,如果没有,会根据

namespace+Servlet的名称来查找XML文件。Spring容器加载时会调用DispatcherServlet的initStrategies方法来完成DispatcherServlet中定义的初始化工作。

 

2、Control设计

Spring MVC的Control主要由HandlerMapping和HandlerAdapters两个组件提供。前者负责映射用户的URL和对应的处理类,其并没规定这个URL与应用的处理类如何映射,HandlerMapping接口中只定义了根据一个URL必须返回一个由HandlerExecutionChain代表的处理链,这个处理链中可以添加任意的HandlerAdapters实例来处理这个URL对应的请求。

 

(1)、HandlerMapping初始化

Spring MVC本身提供了很对HandlerMapping的实现,默认使用的是BeanNameUrlHandlerMapping,可以根据Bean的name属性映射到URL中。

 

如何将请求的URL映射到定义的Bean中?

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

 

(2)、HandlerAdapter初始化

HandlerAdapter可以帮助自定义各种Handler。

注:现今最常用的还是RequestMappingHandlerAdapter这一适配器

相关参考:https://www.cnblogs.com/haoerlv/p/8692988.html

 

Handler的初始化过程是:创建一个HandlerAdapter对象,将这个HandlerAdapter对象保存在DispatcherServlet的handlerAdapters集合中。当Spring MVC将某个URL对应到某个Handler时,在handlerAdapters集合中查询那个handlerAdapters对象supports这个Handler,handlerAdapter对象将会被返回,用了调用这个handlerAdapter接口对应的方法。如果这个handlerAdapter对象是SimpleControllerHandlerAdapter,将调用Controller接口的public ModelAndView handle(HttpServletRequest request, HttpServletResponse reponse,Object handler )方法。如果用户没有自定义HandlerAdapter的实现类,Spring MVC框架将提供默认的四个HandlerAdapter实现类。

 

(3)、Control的调用逻辑

 

整个Spring MVC的调用是从DispatcherServlet的doService方法开始的,在doService方法中会将ApplicationContext、localeResolver、themeResolver等对象添加到request中便于在后面使用。接着就是调用doDispatch方法,这个方法是主要的处理用户请求的地方。

 

Control的处理逻辑关键就是在DispatcherServlet的handlerMappings集合中根据请求的URL匹配每个HandlerMapping对象中的某个Handler,匹配成功后将会返回这个Handler的处理链HandlerExecutionChain对象,而这个HandlerExecutionChain对象中将会包含用户自定义的多个HandlerInterceptor对象。HandlerInterceptor接口中定义的三个方法中,preHandle和postHandle分别在Handler的执行前和执行后执行,afterCompletion在View渲染完成、在DispatcherServlet返回之前执行。这里需要注意的地方是,当preHandle返回false时,当前的请求将在执行完afterCompletion后直接返回,Handler也将不再执行。

 

看看HandlerExecutionChain类的getHandler方法你会发现返回的是Object对象,所以在这里Handler对象是没有类型的,Handler的类型是由HandlerAdapter决定的。DispatcherServlet会根据Handler对象在其handlerAdapters集合中匹配哪个HandlerAdapter实例支持该Handler对象。接下去执行Handler对象的相应方法了,如该Handler对象的相应方法返回一个ModelAndView对象接下是就去执行View渲染。

Control的调用逻辑时序图如下:

 

整体调用流程:

 

3、Model设计

 

如Handler对象返回了ModelAndView对象,那么说明Handler需要传一个Model实例给View去渲染模板。除了渲染页面需要Model实例,在业务逻辑层通常也有Model实例,但是如今介绍的Model实例是针对模板渲染来说的。

 

ModelAndView对象是连接业务逻辑层与View展现层的桥梁,对Spring MVC来说它也是连接Handler与View的桥梁。ModelAndView对象顾名思义会持有一个ModelMap对象和一个View对象或者View的名称。ModelMap对象就是执行模板渲染时所需要的变量对应的实例,如JSP的通过request.getAttribute(String)获取的JSTL标签名对应的对象、Velocity中context.get(String)获取$foo对应的变量实例。

 

ModelMap其实也是个Map,Handler中将模板中需要的对象存在这个Map中,然后传递到View对应的ViewResolvers中,不同的ViewResolvers会对这个Map中的对象有不同的处理方式,如Velocity中将这个Map保存到org.apache.velocity.VelocityContext中,而对于freemarker模板引擎来说将ModelMap包装成freemarker.template. TemplateHash Model。对于JSP来说,将每个ModelMap中的元素分别设置到request.setAttribute (modelName, modelValue)中

 

4、View设计

 

对Spring MVC的View模块来说,其由两个组件来支持,分别是RequestToViewName Translator和ViewResolver。RequestToViewNameTranslator支持用户自定义对ViewName的解析,如将请求的ViewName加上前缀或者后缀,或者替换成特定的字符串等。而ViewResolver用于根据用户请求的ViewName创建合适的模板引擎来渲染出最终的页面,ViewResolver会根据ViewName创建一个View对象,调用View对象的void render(Map model, HttpServletRequest request, HttpServletResponse response)方法渲染出页面。

 

viewNameTranslator的初始化工作比较简单,只是让Spring创建的Bean的对象保存在DispatcherServlet的viewNameTranslator属性中。下面看看ViewResolver的初始化过程。

ViewResolver接口有个抽象的实现类AbstractCachingViewResolver,这个类定义了一个抽象方法View loadView(String viewName, Locale locale),根据viewName创建View对象。ViewResolver相关的类结构图如图所示。

UrlBasedViewResolver类实现了AbstractCachingViewResolver抽象类,通过设置ViewClass来创建View对象。如果使用FreeMarkerViewResolver类,会将ViewClass设置为FreeMarkerView.class;使用VelocityViewResolver类,会将ViewClass设置为Velocity View.class。InternalResourceViewResolver类可以通过注入的方式设置ViewClass属性来初始化自定义的View对象。

 

由于AbstractCachingViewResolver抽象类也继承了WebApplicationObjectSupport,所以所有的AbstractCachingViewResolver子类可以通过覆盖initApplicationContext方法在Spring MVC框架启动时完成初始化工作。如FreeMarkerViewResolver和VelocityView Resolver就是在启动调用setViewClass方法时设置ViewClass属性的。

下面看一下Spring MVC解析View的逻辑,如图所示是渲染JSP页面的时序图。

 

JSP的ViewResolver对应的是InternalResourceViewResolver类,当调用resolveView Name方法时会调用createView方法,将ViewClass属性对应的InternalResourceView类实例化。最后调用InternalResourceView的render方法渲染出JSP页面。

 

5、框架设计最基本的原则

 

(1)、OCP(开闭原则):对扩展开发、对修改关闭。

(2)、LSP(里氏代换原则):凡是基类能使用的地方,子类也一定能使用。

(3)、DIP(依赖倒转原则):要依赖于抽象,不要依赖于具体。

(4)、LSP(开闭原则):接口尽量单一,只代表一个角色。

(5)、CARP(开闭原则):尽量使用合成/聚合复用,尽量不要使用继承。

(6)、LOD(开闭原则):一个对象应当对其他对象的细节有尽可能少的了解。

 

以上图来自《深入分析Java Web技术内幕》

 

参考书籍:

《深入分析Java Web技术内幕》

 

参考博客:

https://blog.csdn.net/yang_guang3/article/details/84196670

 

猜你喜欢

转载自blog.csdn.net/zangdaiyang1991/article/details/85124727