第二讲SpringSpring-MVCSpring-Boot三者之间的区别与联系

40次阅读

共计 6960 个字符,预计需要花费 18 分钟才能阅读完成。


Spring Framework 的诞生让开发人员的工作从石器时代跨域到了工业时代,你是否还能记起手撸 Servlet 和 JDBC 的岁月?,你是否还对 Struts1 以及 Struts2 莫名其妙的 404 错误记忆犹新?从 2004 年 3 月 Spring 1.0 发布到至今,Spring 的发展已经走过了 15 个年头,其创造的价值让人瞩目。今天,带着这样一个背景来梳理一下 Spring Framework,Spring MVC 和 Spring Boot 三者之间的区别。

我们使用 Spring 家族的系列产品这么长时间,不禁会问这样几个问题:Spring Framework 是什么?Spring MVC 是什么?Spring Boot 又是什么?它们被设计出来的目的是什么?

你需要了解的知识

在接下来的内容中,将梳理这样几个知识点:

  • Spring Framework 基本概述
  • Spring Framework 主要解决的问题是什么?
  • Spring MVC 基本概述
  • Spring MVC 主要解决的问题是什么?
  • Spring Boot 主要解决的问题是什么?
  • Spring,Spring MVC 和 Spring Boot 三者之间的区别是什么?

Spring Framework 解决了哪些核心问题?

当你仔细思考这个问题的时候你会发现,很多地方它都有渗透到,貌似一个 Spring 就可以撑起开发的半边天,以至于很难一下子回答这个问题。那 Spring Framework 到底解决了哪些核心问题?

Spring Framework 最重要也是最核心的特性是依赖注入。所有的 Spring 模块的核心就是 DI(依赖注入)或者 IoC(控制反转)。

依赖注入或控制反转是 Spring Framework 最大的特性,当我们正确使用 DI(依赖注入)或 IoC 时,可以开发出一个高内聚低耦合的应用程序,而这一一个低耦合的应用程序可以轻松的对其实施单元测试。这就是 Spring Framework 解决的最核心的问题。

无依赖注入

请考虑这一一个案例:UserAction 依赖于 UserService 来获取用户信息,在没有依赖注入的情况下,我们需要手动在 UserAction 中实例化一个 UserService 对象,这样的手工作业意味着 UserAction 和 UserService 必须精密的联系在一起,才能正常工作。如果一个 Action 需要多个 Service 提供服务,那实例化这些 Service 将是一个繁重的工作。下面我们给出一个不使用依赖注入的代码片段加以说明:

UserService.java

public interface UserService{User profile();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService{
    @Override
    User profile(){// TODO}
}

UserAction.java

@RestController
public class UserAction{private UserService userService = new UserServiceImpl();
    // other services...
    
    @GetMapping("/profile")
    public User profile(){return userService.profile();
    }
}

引入依赖注入

引入依赖注入将会使整个代码看起来很清爽。为了能够开发出高内聚低耦合的应用程序,Spring Framework 为我们做了大量的准备工作。下面我们使用两个简单的注解 @Component@Autowired来实现依赖注入。

  • @Component : 该注解将会告诉 Spring Framework, 被此注解标注的类需要纳入到 Bean 管理器中。
  • @Autowired : 告诉 Spring Framework 需要找到一与其类型匹配的对象,并将其自动引入到所需要的类中。

在接下来的示例代码中,我们会看到 Spring Framework 将为 UserService 创建一个 Bean 对象,并将其自动引入到 UserAction 中。

UserService.java

public interface UserService{User profile();
}

UserServiceImpl.java

@Component
public class UserServiceImpl implements UserService{
    @Override
    User profile(){// TODO}
}

UserAction.java

@RestController
public class UserAction{
    @Autowired
    private UserService userService;
    // other services...
    
    @GetMapping("/profile")
    public User profile(){return userService.profile();
    }
}

对比上下两部分的代码,你是否发现了他们之间的区别?Action 所依赖的 Service 的初始化工作全部交由 Spring Framework 来管理,我们只需要在适当的地方向 Spring Framework 索取想要服务即可。这就好比当我想要吃薯片的时候,我不需要自己亲自种土豆,施肥,收获 … 清洗,切片 … 一直到最后炸土豆片,想想都觉得累,而更简单的方法是直接去超市购买自己想要的薯片即可。

Spring Framework 还有其他的核心特性吗?

1:衍生的特性

Spring Framework 的依赖注入是核心中的核心,在依赖注入核心特性的基础上,Spring Framework 还衍生出了很多的高级模块:

  • Spring JDBC
  • Spring MVC
  • Spring AOP
  • Spring ORM
  • Spring JMS
  • Spring Test

对于这些新的高级模块,可能会产生这一一个问题:它们是否是一个全新的功能?答案是否定的,在不使用 Spring Framework 的情况下,我们依然能够使用 JDBC 连接数据库,依然能够对视图和数据模型进行控制,依然能够使用第三方的 ORM 框架。那 Spring Framework 干了什么?Spring Framework 站在巨人的肩膀上,对这些原生的模块进行了抽象,而抽象可以带来这样一些好处:

  • 减少了应用中模板代码的数量
  • 降低了原生框架的技术门槛
  • 基于依赖注入特性,实现了代码的解耦,真正的高内聚、低耦合
  • 更细粒度的单元测试

这样的好处是显而易见的,比如与传统的 JDBC 相比,使用 JDBCTemplate 操作数据库,首先是代码量小了,其次是我们不需要再面对恐怖的 try-catch。

2:优秀的集成能力

Spring Framework 还具备另外一个重要特性,那就是能够快速的与其他三方框架进行整合。与其自己造轮子,还不如想办法将好的轮子整合在一起,我想这句话应该可以用来概况 Spring Framework 这一特性。Spring Framework 对于整合其他的框架,给出了不错的解决方案,下面将列举一些常见的方案:

  • 与 Hibernate ORM 框架整合
  • 与 MyBatis 对象映射框架整合
  • 与 Junit 单元测试框架整合
  • 与 Log4J 日志记录框架整合

Spring MVC 是什么?

Spring MVC 提供了构建 Web 应用程序的全功能 MVC 模块,实现了 Web MVC 设计模式以及请求驱动类型的轻量级 Web 框架,即采用了 MVC 架构模式的思想,将 Web 层进行职责解耦。基于请求驱动指的是使用 请求 - 响应 模型,视图与数据模型分离,以简化 Web 应用的开发。

使用 Spring MVC 提供的 Dispatcher Servlet,ModelAndView 和 ViewResolver 等功能,可以轻松的开发出一个 Web 应用程序。

Spring Boot 出现的原因是什么?

我们都知道,使用 Spring Framework 来开发应用程序,需要进行大量的配置工作以及依赖包的管理,工作繁重而且极易出现配置错误,尤为明显的是依赖包之间的版本冲突问题。

举一个简单的案例,当我们使用 Spring MVC 来开发 Web 应用程序时,我们大致需要经历这样几个步骤:

  • 1:配置组件扫描的包路径

  • 2:配置对应的 Servlet 程序

  • 3:配置视图解析器

  • 4:配置页面模板引擎(JSP、Freemarker 等)

下面给出了一个小范围的举例:

...
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/pages</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>
<mvc:resources mapping="/static/**" location="/static/"/>
...

此外,我们还需要配置先关的 Servlet 处理程序,它们大致是这样的:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/todo-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

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

如果我们的应用程序还需要链接数据,则还需要配置数据源,实体对象管理器,事务管理器等众多配置:

<bean id="datasource" class="">
    ...
</bean>
<bean id="entityManagerFactory" class="">
    ...
</bean>
<bean id="transactionManager" class="">
    ...
</bean>
...

面对众多的配置文件,我们需要花费大量的时间去处理,这时你可能会问,我为什么要花费那么多的时间去管理 Spring 的配置工作?不是应该专注于应用本身的业务逻辑吗?现在,有了 Spring Boot,这些烦心事就不需要你去操心了。

1:Spring Boot 的自动化配置能力

我为什么会把 Spring Boot 的自动化配置能力放在第一位,因为它极大的降低了我们使用 Spring Framework 所付出的成本。这是 Spring Boot 的自动化配置是一个最具价值的解决方案。

这难道不值得我们拍案叫好吗?如果你想要开发一个 Web 应用程序,你需要做的事情就是将 Spring Boot Web 包引入到项目的类路径下,Spring Boot 就可以帮你解决后续的大多数配置工作。

  • 如果 Hibernate 的依赖被放到了类路径上,Spring Boot 会自动配置数据源
  • 如果 Spring MVC 的依赖被放到了类路径上,Spring Boot 又会自动配置 Dispatcher Servlet

当 Spring Boot 检测到有新的依赖包添加到类路径上,Spring Boot 会采用默认的配置对新的依赖包进行设置,如果我们想自己配置依赖包时,只需要手动覆盖默认的配置项即可。

  1. Spring Boot 扫描类路径上可用的框架信息
  2. 获取应用程序现有的配置信息
  3. 如果应用程序没有提供框架的配置信息,Spring Boot 将采用默认的配置来配置框架,这就是 Spring Boot 的自动配置特性(Auto Configuration)

2:Spring Boot Starter 项目

在传统模式的开发过程中,我们需要反复的确认应用程序所需要的第三方 JAR 包,以及这些 JAR 的版本和依赖关系。例如,现在我们打算开发一款 Web 应用程序,应用程序大概需要如下的一些依赖包:Spring MVC,Jackson Databind(用于数据绑定),Hibernate-Validator(用于服务端的数据校验)和 Log4j(用于日志记录)。现在,我们需要去下载对应的 jar 包到应用程序中,并且还需要处理依赖包之间版本冲突的问题。

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.2.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.3</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.0.2.Final</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

这不是一件简单的事情,特别是发生版本冲突的时候,让应用程序能够正常运行起来就需要花费一定的时间。

Spring Boot Starter 是一组用于管理依赖关系的描述符,通过这些描述符,我们可以在应用程序中轻松的管理依赖包,你可以以开箱即用的方式获取想要的依赖包,而无需去 Maven 仓库总检索对应的依赖,并将依赖配置复制粘贴到应用程序的 pom 文件中。例如,如果你想要使用 Spring 和 JPA 进行数据库访问,只需要在 pom 中添加 spring-boot-starter-data-jpa 依赖项就可以。

现在,如果我们想要开发一个 Web 应用程序,使用 Spring Boot Starter Web 依赖会是一个不错的选择。我们可以通过使用 Spring INitializr 快速构建一个 Web 应用程序,并将 Spring Boot Starter Web 添加到项目中。此时我们的 pom 文件只需要很少的配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Boot Starter Web 会为我们预装如下的一些依赖:

  • Spring : core,beans,context,aop
  • Web MVC : Spring MVC
  • Jackson : JSON Binding
  • Validation : Hibernate Validator,Validation API
  • Embedded Servlet Container: Tomcat
  • Logging : logback,slf4j

对于开发人员而言,我们不需要去担心这些依赖项的管理工作以及解决他们之间的兼容性问题,Spring Boot 已经帮我们解决了。

Spring Boot 的核心目标

Spring Boot 的核心目标在于快速实现生产就绪的应用程序,这将包含这样几个部分:

  • 执行器:启用高级监控和跟踪应用程序功能
  • 嵌入式服务器:Spring Boot 已经内置了多个 Web 服务器,如 Undertow,jetty,tomcat,因此我们不需要再额外的配置服务器,就可以完成应用程序的调试工作。
  • 默认的异常处理机制
  • 开箱即用的依赖项管理机制
  • 自动化配置

总结

通过上述的梳理,我们可以看到,Spring Framework 是一个提供了 DI(依赖注入)和 IoC(控制反转)的开发框架,使用 Spring Framework 可以帮助我们开发出高内聚,低耦合的应用程序,Spring MVC 是在 Spring Framework 基础上发展出来的基于 MVC 模式的全功能 Web 开发框架,实现了 Model,View 和 Controller 之间的职责解耦;Spring Boot 为我们提供了一个能够快速使用 Spring Framework 的优秀解决方案,通过最小化的配置,我们就可以使用 Spring Framework,严格意义上讲,Spring Boot 并不是某种框架,它只是为开发人员提供了一个更好的更方便的使用 Spring Framework 的解决方案。

作者:谭朝红
原文:Spring&Spring MVC&Spring Boot 三者之间的区别与联系

转载请联系作者本人,未经允许,请勿做任何商业行为

正文完
 0