Java [SpingBoot 从零单排系列] 简述 SpringMVC ,以及它与 SpringBoot 的差异

战 神 · 2019年05月23日 · 最后由 战 神 回复于 2019年05月27日 · 2554 次阅读

前文回顾

呵呵哒,老衲又来了。。。回顾一下上一篇文章,主要总结了 Spring 中的两点常考特性,IoCAOP 的原理。 这里顺便谈一下写帖子的初衷,我翻了绝大多数的社区工具贴,发现一个通病就是 ,大家只去展现自己做的工具多么多么好用,而极少的介绍自己做的过程以及遇到的坑,甚至是底层的原理,其实这对学习者本身也是不利的,我个人建议是自己把自己所做的平台的实现原理由浅入深一点一点的撕开,多说做法与实现,这不仅仅是对于社区的贡献,也是突出一种开源精神,因为比你懂的人太多太多了,你所做的东西也许会有更好的方式去实现,一方面做自我总结与回溯,另一方面可以收获更多的建议与知识。所以我希望尽可能的能描述工具底层原理以及自己的想法,我也相信真的有价值的东西都是从本质中来的,且挖得越深越好,只有这样,才能够真的从零开始自己去动手做一个东西,给你带来成就感同时学到知识。

正文

言归正传,本次再次以常见面试点切入,来叙述一下SpringMVC 与 SpringBoot 的差异

  • SpringMVC 与 SpringBoot 的差异。

这里再说一下,为什么要聊这两者的差异 而不直接就开门见山的去说 SpringBoot。作为开发者来说,SpringBoot 的由来是 SpringMVC 针对应用开发者简化流程以及优化后的一个应用框架,所以如果你在学习 SpringBoot,那 SpringMVC 是你绕不开的一个必经之路。

之前我们说到 Spring 框架的主要特征以及它的原理,如果我们想要了解这两者之间的差异,那首先得了解SpringMVC是个啥东东?

Spring MVC 是 Spring 的一部分,即 Spring 框架中的一个模块,当 Spring 出来以后,大家觉得很好用,于是按照这种模式设计了一个 MVC 框架(一些用 Spring 解耦的组件),主要用于开发 WEB 应用和网络接口,它通过Dispatcher Servlet, ModelAndViewView Resolver,让应用开发变得很容易。

先来看一下什么是 MVC,以下图为例 :

  • 关键词 M : model 模型V : view 视图C:controller 控制器

接着我们再来看一下 SpringMVC 的内部组件交互图, 图画得有点丑,包涵一下。

  • 关键词:

DispatcherServlet :前端控制器,相当于 SpringMVC 的大脑,类似中央处理器的概念,负责接收请求与返回响应,把请求给到 controller, DispatcherServlet 继承自 HttpServlet,它遵循 Servlet 里的 “init-service-destroy” 三个阶段.

a)截获符合特定格式的 URL 请求

b)初始化 DispatcherServlet 上下文对应的 WebApplicationContext,并将其业务层以及持久层的 WebApplicationContext 建立关联

c)通过源码中的 initStrategies()方法可以看到,它将在 WebApplicationContext 初始化后自动执行,自动扫描上下文的 Bean,根据名称或类型匹配的机制查找自定义的组件,如果没有找到则会装配一套 Spring 的默认组件。

HandlerMapping: 处理器映射器,能够完成客户请求到 Controller 的映射,DispatcherServlet 接收 request,然后从 HandlerMapping 查找处理 request 的 controller ,如上图步骤 2.

HandlerAdapter:处理器适配器,典型的 java 适配器模式,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当 handlerMapping 获取到执行请求的 controller 时,DispatcherServlte 会根据 controller 对应的 controller 类型来调用相应的 HandlerAdapter 来进行处理。

Controller:这里我们实现的 Controller 接口的对象即为(Handler),需要为并发用户处理请求,我们的 Controller 里面会标注请求的 url 并声明请求方式,因此实现 Controller 接口时,因此要考虑线程安全和可以重用的问题,一旦 Controller 处理完用户请求,则返回 ModelAndView 对象给 DispatcherServlet 前端控制器,ModelAndView 中包含了模型(Model)和视图(View)。

从宏观来看,DispatcherServlet 是整个 Web 应用的控制器,从微观来看,Controller 是单个 Http 请求的控制器。

View resolver:视图解析器, ViewResolver 的主要作用是把一个逻辑上的视图名称解析为一个真正的视图,解析视图的核心工作是查找模板文件(如 /WEB-INF/freemarker/template 项目目录下的 xxx.ftl)和视图类型 (SpringMVC 里申明的对象,一般使用较多的就是 InternalResourceView ,利用它来展现 jsp ,但是当我们使用 JSTL 的时候我们必须使用 JstlView) 。 SpringMVC 中用于把 View 对象呈现给客户端的是 View 对象本身,而 ViewResolver 把逻辑视图名称解析为对象的 View 对象 。 View 接口的主要作用是用于处理视图,然后返回给客户端。

描述完 SpringMVC 的工作流程后,我们再来看一下 SpringBoot, SpringBoot 沿用了 SpringMVC 的工作流程,如之前所说,SpringBoot 的到来是为了让开发者更加专注于应用逻辑本身,简化了 SpringMVC 带给我们繁琐而复杂的配置。

我们通过项目结构来看一下,下图是 SpringMVC 的基本配置文件数

再来看看 SpringBoot 的基本配置文件。是不是感觉少了很多?

首先一个最最明显的特征就是 :SpringBoot 不再像 SpringMVC 那样打包成 war 包 , 还要额外需要安装 Tomcat 容器来部署,它本身是 jar 的形式就已经支持 tomcat 部署启动,
如 你已经打包好了你的 SpringBoot 服务,你只需要 :

java -jar  xxxx.jar 

可以通过实现 EmbeddedServletContainerCustomizer 来变更 tomcat 里的一部分配置。
代码如下 :

@Component
public static class CustomServletContainer implements EmbeddedServletContainerCustomizer{
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.setPort(8888);//1
        container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404.html"));
        container.setSessionTimeout(10,TimeUnit.MINUTES);
    }
}

然后我们具体我们再来看一下官方文档的说明,SpringBoot 的自动配置多了哪些东西?

翻译如下 :

  • 1.引入 ContentNegotiatingViewResolver 和 BeanNameViewResolver beans。
    • 自动配置了 ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(是转发还是重定向)页面 )
    • ContentNegotiatingViewResolver:代理给不同的 ViewResolver 处理不同的 View。
    • 我们可以自己给容器中添加一个视图解析器,SpringBoot 会自动的将其组合进来;因为从源码分析可以返现 ContentNegotiatingViewResolver 所组合的视图解析器都是从容器中获取的。 由此我们可以得出,spring-mvc.xml 的配置文件中的项目得到了优化,视图模板不需要再配置了。

spring-mvc.xml 中的 ViewResolver 配置,比如之前我们需要为 viewClass 指定 JSTL 的视图模板

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
         <property name="messageConverters">
              <list> 
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />           
              </list>
         </property>
    </bean>
    <bean id="jstlViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
            >
            <property name="viewClass">
                <value>org.springframework.web.servlet.view.JstlView</value>
            </property>
            <property name="prefix" value="/jsp/" />
            <property name="suffix" value=".jsp" />
    </bean>
</beans>
  • 2.对静态资源的支持,包括对 WebJars 的支持。

当使用 Spring Boot 来开发一个完整的系统时,我们往往需要用到前端页面,这就不可或缺地需要访问到静态资源,比如图片、css、js 等文件。

  • WebMvcConfigurerAdapter 接口中的 addResourceHandlers 可以对静态资源进行配置 ,实际新版本的 Spring 中, WebMvcConfigurerAdapter 被废弃了 ,采用 WebMvcConfigurer 接口
  • 类路径文件:/static、/public、/resources、/META-INF/resources 静态文件映射为/*,可以直接通过/context/访问 这个功能会常用,这里简单介绍一下 , 先说下默认映射的文件夹有:

classpath:/META-INF/resources
classpath:/resources
classpath:/static
classpath:/public

举个场景,比如我们 假设 映射的 url 为 /static静态资源存放在 classpath:/static
部署后请求自身服务的资源 /static/style.css, 则会直接查找 classpath:/static/style.css

在 SpringBoot 中我们可以通过修改 application.yml 中的字段值 spring.mvc.static-path-pattern来修改默认的映射 ,当然也可以通过代码

代码如下,这里我们推荐 继承 WebMvcConfigurationSupport ,并通过@EnableWebMvc 注解来修改默认配置:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport{
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

    // 映射 /static 的请求到 classpath 下的 static 目录

    registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static");
    }
}
  • 3.自动注册 Converter,GenericConverter,Formatter beans。

    • 定义类实现 Convert(自定义转换器,如将字符串转对象)、GenericConvert(较前者更为复杂,支持在多个不同的原类型和目标类型之间进行转换)、Formatter 即会自动注册
  • 4.对 HttpMessageConverters 的支持。

    • 这里举个例子,比如你在应用中定义一种返回数据未 xx:xx 格式的数据,前端可以配置 contentType:"application/xx" ,并通过 HttpMessageConverters 来约束这个对象响应,这里在 SpringBoot 中得到了很好的支持。
  • 5.自动注册 MessageCodeResolver。

    • MessageCodeResolver(根据对应的错误生成响应 )
  • 6.对静态 index.html 的支持。

    • Spring boot 的默认首页的设置,默认会访问 resources/static 的 index.html,不需要任何配置就能访问, 就像常规 web 项目默认访问 web.xml 下的 index.jsp 是一样的 。
  • 7.对自定义 Favicon 的支持。

    • Favicon 即浏览器的页签图片,可以在 application.yml 中进行自定义配置(看起来似乎无关紧要。。。。)
  • 8.字段使用 ConfigurableWebBindingInitializer bean

    • ConfigurableWebBindingInitializer : 其主要作用就是 初始化 WebDataBinder;将请求的参数转化为对应的 JavaBean,并且会结合上面的类型、格式转换一起使用。

除此之外,在 Servlet 3.0 里,web.xml 这个配置得到了简化。可以通过 java 配置(注解等)省去 web.xml 配置,SpringBoot 遵循这一规则,通过 SpringServletContainerInitializer 初始化自动加载,DispatcherServletAutoConfiguration 的自动配置 DispatcherServlet,免去了 web.xml 的复杂配置。

在 SpringMVC 中 web.xml 针对 DispatcherServlet 的配置

<servlet>
      <servlet-name>xxx</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>         
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>
              classpath:/spring-mvc.xml               
          </param-value>
      </init-param>       
      <load-on-startup>1</load-on-startup> 
  </servlet>

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

   <servlet>
  <servlet-name>init</servlet-name>
  <servlet-class>xxx.Initializer</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>

结尾

写完之后重新再浏览一遍,并且站在一个新手的角度再来看这个文章,如果我是个新手,我觉得我完全没办法坚持看下去,我觉得绝大多数人都会喜欢白嫖式的操作,拿起框架就干,遇到问题再查。但是我还是决定把这篇在这里先发出来,毕竟犹豫就会败北,果断才能白给。目的是为了在后面写到一些场景用法的时候再通过这一篇的一些原理来回顾,总体来说,这一篇会更加适合于回顾而不是先知。

本人非常喜欢杠精,请尽情抬杠

共收到 2 条回复 时间 点赞
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册