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

战 神 · May 23, 2019 · Last by 战 神 replied at May 27, 2019 · 686 hits

前文回顾

呵呵哒,老衲又来了。。。回顾一下上一篇文章,主要总结了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 条回复 时间 点赞
战 神 #2 · May 27, 2019 作者

。。超越的速度。。

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up