Spring MVC框架的设计理念

要使用Spring MVC,只需要在web.xml中配置一个DispatcherServlet(前端控制器),再定义一个dispatcherServlet-servlet.xml(Spring MVC 默认配置文件名)的配置文件,一个基于Spring MVC的应用就创建好了。使用Spring MVC也比较简单,我们只需要扩展一个路径映射关系(@requestMapping映射);定义一个视图解析器(定义一个Handler);再定义一个业务逻辑的处理流程(Handler中的处理方法),Spring MVC 就能够帮你完成所有的MVC功能

Spring MVC总体设计

DispatcherServlet类继承了HttpServlet,在Servlet的init方法调用时DispatcherServlet执行Spring MVC的初始化工作。DispatcherServlet进行初始化是在initStrategies方法中定义的8件事

  • initMultipartResolver:初始化MultipartResolver,用于文件上传。如果有文件上传,会自动将HttpServletRequest包装成DefaultMultipartHttpServletRequest,并且将每个上传的内容封装成CommonsMultipartFile对象
  • initLocaleResolver:用于处理应用的国际化问题。通过解析请求的Local和设置响应的Local来控制应用中的字符编码问题
  • initThemeResolver:用于定义一个主题,例如可以根据用户的喜好来设置用户访问页面的样式,可以将这个样式作为Theme Name保存,保存在请求的COokie或者服务端的Session中,以后每次请求根据这个Theme Name返回特定的内容
  • initHandlerMappings:用于定义用户设置的请求映射关系,将用户请求的URL映射成以一个个Handler实例。如果没有定义,将获取DispatcherServlet.properties文件默认的两个HandlerMapping,分别是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping
  • initHandlerAdapters:用于根据Handler的类型定义不同的处理规则,Spring MVC会根据设定的Adapter去调用Handler实例。同样HandlerAdapters也必须定义,如果没有定义,将获取DispatherServlet.properties文件中默认的4个HandlerAdapter,分别是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,ThrowawayControllerHandlerAdapter和AnnotationMethodHandlerAdapter
  • initHandlerExceptionResolvers:当Handler处理错误时,会通过这个Handler来统一处理,默认的实现类是SimpleMappingExceptionResolver,将错误日志记录在log文件中,并且转到默认的错误页面
  • initRequestToViewNameTranslator:将指定的ViewName按照定义的RequestToViewNameTranslator替换成想要的格式,例如加上前缀或后缀
  • initViewResolvers:用于将View解析成页面,在ViewResolvers中可以设置多个解析策略,如可一个根据JSP来解析,或者FreeMarket,Velocity等等。默认的解析策略是InternalResourceViewResolver,按照JSP页面来解析

从上述初始化策略可以看出,在一个请求中可能需要我们扩展的地方都定义了扩展点,只需要实现相对应的接口类,并创建一个Spring Bean就能扩展Spring MVC框架了

在Spring MVC框架中,有3件事事用户必须定义和扩展的:定义URL映射规则,实现业务逻辑代码的Handler对象,渲染模板资源。而连接实现业务逻辑代码的Handler对象和渲染模板资源的纽带就是Model模型

DispatcherServlet启动过程中做了哪些事情

HttpServlet初始化调用了HttpServletBean的init方法,该方法的作用是获取Servlet中的init参数,并创建一个BeanWrapper对象,然后让子类真正执行BeanWrapper的初始化工作。但是HttpServletBean的子类FrameworkServlet和DispatherServlet都没有覆盖initBeanWrapper(bw)方法,所以创建的BeanWrapper对象没有任何作用,Spring MVC容器也不是通过BeanWrapper来创的

Spring MVC的容器创建是在FrameworkServlet的initServletBean()方法中完成的,这个方法会创建WebApplicationContext对象,并调用其refresh()方法来完成配置文件的加载,配置文件的加载同样是先查找Servlet的init-param参数中设定的路径,如果没有,会根据dispatcherServlet-servlet.xml名称来查找XML文件。Spring MVC容器在加载过程时会调用DispatcherServlet的initStrategies方法完成初始化工作,也就是上述8个组件的初始化,8个组件对应8个Bean都保存在DispatcherServlet类中

Control设计

Spring MVC的Control主要由HandlerMapping和HandlerAdapters两个组件提供。HandlerMapping负责映射用户的URL和对应的处理类,HandlerMapping没有规定这个URL与应用的处理类如何映射,在HandlerMapping接口中只是定义了根据URL必须返回一个由HandlerExecutionChain代表1的处理链,我们可以在这个处理链中添加任意的HandlerAdapter实例来处理这个URL对应的请求,这个设计思路和在Servlet规范中的Filter处理是类似的

HandlerMapping初始化

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

以上使用BeanNameUrlHandlerMapping需要在Spring MVC配置文件中配置Bean的方式,来映射name与其对应的class处理类,而现在更多使用@RequestMapping注解来使用Spring MVC

HanlerMapping的作用就是帮助我们管理URL和处理类的映射关系,简单来说就是,将一个或者多个URL映射到一个或者多个Spring Bean中

Spring MVC提供了一个HandlerMapping的抽象类AbstractHandlerMapping,AbstractHandlerMapping同时还实现了Ordered接口并继承了WebApplicationObjectSupport类,可以让HandlerMapping通过设置setOrder方法提高优先级,并通过覆盖initApplicationContext方法实现初始化的一些工作

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

如果用户没有自定义HandlerMapping的实现类,Spring MVC默认提供了2个实现类(BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping)

HandlerAdapter初始化

Spring MVC首先要帮助我们把URL对应到一个Handler,那么这个Handler必定符合某种规则,最常见可以想到的就是我们所有的Handler都实现某个接口,然后就调用这个接口中定义的特殊方法(Struct的实现),但是Spring MVC采用另一种方式,也就是HandlerAdapter,我们的URL对应的Handler可以实现任何接口,每个接口可以定义不同的方法完全没有问题。Spring MVC的HandlerAdapter机制可以让Handler的实现更加灵活,不需要和其他MVC框架一样与某个接口绑定起来,不同的Handler甚至可以指定不同的Adapter去处理

HandlerAdapter初始化就是创建一个HandlerAdapter对象,将这个对象保存在DispatcherServlet的handlerAdapters集合中。当Spring MVC将某个URL对应到某个Handler时,在handlerAdapters集合中查询哪个handlerAdapter对象兼容这个Handler,这个handlerAdapter对象将被返回,并调用这个handlerAdapter接口对应的方法

如果用户没有自定义HandlerAdapter的实现类,Spring MVC默认提供了4个实现类(HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,ThrowawayControllerHandlerAdapter和AnnotationMethodHandlerAdapter)

Control的逻辑调用

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

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

Handler的类型是由HandlerAdapter决定的,DispatcherServlet会根据Handler对象在其handlerAdapters集合中匹配哪个HandlerAdapter实例来支持该Handler对象,接下来执行Handler对象的相应方法,如该Handler对象的相应方法返回一个ModelAndView对象,就需要去执行View渲染

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.TemplateHashModel,对于JSP来说就将每个ModelMap中的元素分别设置到request.setAttribute(modelName, modelValue)中

View设计

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

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

ViewResolver接口有个抽象的实现类AbstractCachingViewResolver,这个类定义了一个抽象方法View loadView(String viewName, Locale local),该方法根据viewName创建View对象。UrlBasedViewResolver类实现了AbstractCachingViewResolver抽象类,通过设置ViewClass来创建View对象。如果使用的是FreeMarkerViewResolver类,则会将ViewClass设置为FreeMarkerView.class;如果使用VelocityViewResolver类,则会将ViewClass设置为VelocityView.class;如果使用InternalResourceViewResolver类,则会将ViewClass设置为InternalResourceView.class

由于AbstractCachingViewResolver抽象类也继承了WebApplicationObjectSupport,所以所有的AbstractCachingViewResolver子类可以通过覆盖initApplicationContext方法在Spring MVC框架启动时完成初始化工作

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

posted @ 2021-02-27 17:51  OverZeal  阅读(144)  评论(0编辑  收藏  举报