Spring Boot的前世今生以及它和Spring Cloud的关系详解。

536 阅读15分钟

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战

要了解Spring Boot的发展背景,还得从2004年Spring Framework1.0版本发布开始说起,不过大家都是从开始学习Java就使用Spring Framework了,所以就不做过多展开。

随着使用Spring Framework进行开发的企业和个人越来越多,Spring 也慢慢从一个单一简洁的小框架编程了一个大而全的开源软件,Spring Framework的边界不断进行扩张,到了现在Spring 几乎可以做任何事情。目前市面上绝大部分的开源组件和中间件,都有Spring对应组件的支持,

你们如果去关注Spring目前的官网,你会发现他的slogan是:Spring makes Java Simple。它让Java的开发变得更加简单。

虽然Spring的组件代码是轻量级的,但是它的配置却是重量级的,Spring 每集成一个开源软件,就需要增加一些基础配置,慢慢的随着我们开发的项目越来越庞大,往往需要集成很多开源软件,因此后期使用 Spirng 开发大型项目需要引入很多配置文件,太多的配置非常难以理解,并容易配置出错,这个给开发人员带来了不少的负担。

大家想象一个场景,就是假如你需要用spring开发一个简单的Hello World Web应用程序,应该要做哪些动作呢?

  • 创建一个项目结构,必然包含依赖Maven或者Gradle的构建文件。
  • 至少需要添加spring mvc和servlet api的依赖
  • 一个web.xml,声明spring的DispatcherServlet
  • 一个启用了Spring MVC的spring 配置
  • 一个控制器类,“以HelloWord”为响应的http请求
  • 一个用于部署应用程序的web应用服务器,比如Tomcat

在整个过程中,我们发现只有一个东西和Hello Word功能相关,那就是控制器(controller),剩下的都是Spring 开发的Web应用程序必须要的通用模版,既然所有Spring Web应用程序都要用到他们,那为什么还要你来提供这些东西呢?

所以,直到2012年10月份,一个叫Mike Youngstrom(扬斯特罗姆)在Spring Jira中创建了一个功能请求,要求在Spring Framework中支持无容器Web应用程序体系结构,他谈到了在主容器引导 spring 容器内配置 Web 容器服务。

jira.spring.io/browse/SPR-…

I think that Spring's web application architecture can be significantly simplified if it were to provided tools and a reference architecture that leveraged the Spring component and configuration model from top to bottom. Embedding and unifying the configuration of those common web container services within a Spring Container bootstrapped from a simple main() method.
我认为,如果要提供从上到下充分利用Spring组件和配置模型的工具和参考体系结构,则可以大大简化Spring的Web应用程序体系结构。在通过简单main()方法引导的Spring容器中嵌入和统一那些通用Web容器服务的配置。

而且Spring 开发团队也意识到了这些问题,急需要一套软件来解决这个问题,而这个时候微服务的概念也慢慢的起来,快速开发微小独立的应用也变得很急迫。

而Spring恰好处在这样一个交叉点上,所以顺势而为在2013年初的时候,开始投入Spring Boot项目的研发,直到2014年4月,Spring Boot1.0版本发布。从那以后,Spring Boot开启了一些列的迭代和升级的过程。

经过7年时间的发展,到目前为止,Spring Boot最新稳定版为2.6.0版本。

Spring Boot的发展

Spring Boot刚出生的时候,引起了很多开源社区的关注,并且也有个人和企业开始尝试使用Spring Boot。 其实直到2016年,Spring Boot才真正在国内被使用起来。我之前在挖财的时候,2015年公司就开始采用Spring Boot来构建基于Dubbo的微服务架构。到现在,Spring Boot几乎是所有公司的第一选择。

Build Anything

Spring Boot被官方定位为“BUILD ANYTHING”,Spring Boot官方的概述是这么描述Spring Boot的。

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
// 通过Spring Boot可以轻松的创建独立的、生产级别的基于Spring 生态下的应用,你只需要运行即可。
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.
//对于Spring平台和第三方库,我们提供了一个固化的视图,这个视图可以让我们在构建应用是减少很多麻烦。大部分spring boot应用只需要最小的Spring 配置即可。

如果大家不习惯看英文文档,可能理解起来比较复杂,翻译成人话就是:Spring Boot能够帮助使用Spring Framework生态的开发者快速高效的构建一个基于Spring以及spring 生态体系的应用。

为了让大家对这句话的理解更加深刻,我们来做两个小实验,一个是基于传统的Spring MVC框架构建一个项目、另一种是使用Spring Boot。

Spring MVC With Spring Boot

通过Spring MVC项目搭建过程来对比Spring Boot的差异和优势。

Spring MVC项目搭建过程

  • 创建一个maven-webapp项目

  • 添加jar包依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    
    spring-context
    spring-context-support
    spring-core
    spring-expression
    spring-web
    spring-webmvc
    
  • 修改web.xml文件

    <context-param><!--配置上下文配置路径-->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!--配置监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    </listener>
    <!--配置Spring MVC的请求拦截-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-patter>
    </servlet-mapping>
    
  • 在resources目录下添加dispatcher-servlet.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 扫描 controller -->
    <context:component-scan base-package="com.gupaoedu.controller" />
    <!--开启注解驱动-->
    <mvc:annotation-driven/>
    <!-- 定义视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    
  • 创建一个Controller

    @Controller
    public class HelloController {
    
        @RequestMapping(method = RequestMethod.GET,path = "/index")
        public String index(Model model){
            model.addAttribute("key","Hello Gupao");
            return "index";
        }
    }
    
  • 修改默认的index.jsp,设置el表达式的解析

    <%@ page language="java" contentType="text/html; charset=utf-8"
             pageEncoding="utf-8" isELIgnored="false" %>
    ${key}
    
  • 运行项目

Spring Boot搭建过程

直接基于start.spring.io这个脚手架搭建即可。

思考和总结

咱们再回到最开始Spring Boot的定义部分,Spring Boot能够帮助使用Spring Framework生态的开发者快速高效的构建一个基于Spring以及spring 生态体系的应用。

再对比两种构建过程,似乎也能够理解Spring Boot的作用了吧。当然它的作用不仅于此,后续会逐步揭开它的真实面目。

通过上面这个案例我们发现,如果没有spring boot,要去构建一个Spring MVC的web应用,需要做的事情很多

  • 引入jar包
  • 修改web.xml,添加监听和拦截
  • 创建spring mvc核心配置文件dispatcher-servlet.xml
  • 创建controller
  • 部署到tomcat

这个过程如果不熟悉,很可能需要1~2个小时,如果是新手,可能需要更长时间。但是spring boot,不管是新手还是老手,都能够分分钟解决问题。

理解约定优于配置

我们知道,Spring Boot是约定由于配置理念下的产物,那么什么是约定由于配置呢?

约定优于配置是一种软件设计的范式,主要是为了减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。

简单来说,就是你所使用的工具默认会提供一种约定,如果这个约定和你的期待相符合,就可以省略那些基础的配置,否则,你就需要通过相关配置来达到你所期待的方式。

约定优于配置有很多地方体现,举个例子,比如交通信号灯,红灯停、绿灯行,这个是一个交通规范。你可以在红灯的时候不停,因为此时没有一个障碍物阻碍你。但是如果大家都按照这个约定来执行,那么不管是交通的顺畅度还是安全性都比较好。

而相对于技术层面来说,约定有很多地方体现,比如一个公司,会有专门的文档格式、代码提交规范、接口命名规范、数据库规范等等。这些规定的意义都是让整个项目的可读性和可维护性更强。

Spring Boot Web应用中约定优于配置的体现

那么在前面的案例中,我们可以思考一下,Spring Boot为什么能够把原本繁琐又麻烦的工作省略掉呢? 实际上这些工作并不是真正意义上省略了,只是Spring Boot帮我们默认实现了。

而这个时候我们反过来思考一下,Spring Boot Web应用中,相对Spring MVC框架的构建而言,它的约定由于配置体现在哪些方面呢?

  • Spring Boot的项目结构约定,Spring Boot默认采用Maven的目录结构,其中

    src.main.java 存放源代码文件

    src.main.resource 存放资源文件

    src.test.java 测试代码

    src.test.resource 测试资源文件

    target 编译后的class文件和jar文件

  • 内置了嵌入式的Web容器,在Spring 2.2.6版本的官方文档中3.9章节中,有说明Spring Boot支持四种嵌入式的Web容器

    Tomcat

    Jetty

    Undertow

    Reactor

  • Spring Boot默认提供了两种配置文件,一种是application.properties、另一种是application.yml。Spring Boot默认会从该配置文件中去解析配置进行加载。

  • Spring Boot通过starter依赖,来减少第三方jar的依赖。

这些就是Spring Boot能够方便快捷的构建一个Web应用的秘密。当然Spring Boot的约定优于配置还不仅体现在这些地方,在后续的分析中还会看到Spring Boot中约定优于配置的体现。

Spring Boot整合Mybatis

实际上Spring Boot的本质就是Spring,如果一定要从技术发展的过程中找到一些相似的对比的话,你们可以对比一下Jsp/Servlet和Spring MVC, 两者都可以用来开发Web项目,但是在使用上,Spring MVC的使用会更加简单。

而Spring Boot和Spring 就相当于当年的JSP/Servlet和Spring MVC的关系。所以它本身并没有所谓新的技术,接下来,我带着大家来通过Spring Boot整合Mybatis实现数据的基本操作的案例,来继续认识一下Spring Boot。

创建Spring Boot 应用

创建一个Web项目

引入项目中需要的starter依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

创建数据库表

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `address` varchar(80) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

配置数据库连接

spring:
  datasource:
    url: jdbc:mysql://192.168.13.106:3306/test_springboot
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

开发数据库访问层

1587536145803

创建实体对象

public class User {
    private int id;
    private String name;
    private String address;
}

创建Mapper

//@Repository可以支持在你的持久层作为一个标记,可以去自动处理数据库操作产生的异常
@Repository
@Mapper
public interface UserMapper {

    User findById(int id);
    List<User> list();
    int insert(User user);
    int delete(int id);
    int update(User user);
}

编写mapper文件

在resource文件目录下创建UserMapper.xml文件,内容如下

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD com.example.Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="resultMap" type="com.example.demo.entity.User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="address" column="address"/>
    </resultMap>

    <select id="findById" resultMap="resultMap" parameterType="java.lang.Integer">
        select * from t_user where id=#{id}
    </select>
    <select id="list" resultMap="resultMap">
        select * from t_user
    </select>
    <insert id="insert" parameterType="com.example.demo.entity.User" keyProperty="id" useGeneratedKeys="true">
        insert into t_user(name,address) values(#{name,jdbcType=VARCHAR},#{address,jdbcType=VARCHAR})
    </insert>
    <delete id="delete" parameterType="java.lang.Integer">
        delete from t_user where id=#{id}
    </delete>
    <update id="update" parameterType="com.example.demo.entity.User">
        update t_user set name=#{name,jdbcType=VARCHAR},address=#{address,jdbcType=VARCHAR} where id=#{id,jdbcType=INTEGER}
    </update>
</mapper>

定义service及实现

public interface IUserService {

    User findById(int id);
    List<User> list();
    int insert(User user);
    int delete(int id);
    int update(User user);
}

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;
}

创建Controller

@RestController
public class Controller {

    @Autowired
    private IUserService userService;

    @GetMapping("/user/{id}")
    public User user(@PathVariable("id") int id){
        return userService.findById(id);
    }

    @GetMapping("/users")
    public List<User> users(){
        return userService.list();
    }

    @PostMapping("/user")
    public String insertUser(User user){
        int row=userService.insert(user);
        return row>0?"SUCCESS":"FAILED";
    }

    @PutMapping("/user")
    public String updateUser(User user){
        int row=userService.update(user);
        return row>0?"SUCCESS":"FAILED";
    }
    @DeleteMapping("/user/{id}")
    public String deleteUser(@PathVariable("id") int id){
        return userService.delete(id)>0?"SUCCESS":"FAILED";
    }

}

修改配置

  • 在Spring 的Main方法上增加以下注解,用来扫描Mybatis的Mapper文件

    @MapperScan("com.example.demo.mapper")
    
  • 配置Mapper配置文件的地址,在application.yml中

    mybatis:
      mapper-locations: classpath:*Mapper.xml
    

    id int,

    name varchar(20),

    address varchar(20)

)

项目打包

  • mvn -Dmaven.test.skip -U clean install
  • java -jar xxx.jar

简单总结

这个代码,我想,大家应该写过无数遍了,而在基于Spring Boot集成Mybatis这个案例中,核心的业务逻辑并没有减少,它只减少了一些繁琐的配置,使得我们更聚焦在业务开发层面。

简单来说,基于Spring Boot的项目中,我们只需要写Controlelr、Service、Dao即可。甚至很多情况下我们dao都不需要管,比如使用mybatis-plus这个插件,就可以省去很多固定的dao层逻辑。

所以实际上,Spring Boot并没有新鲜的东西,因此你看到市面上大部分讲spring boot的书,这些书我几乎都看过,基本上都是讲解Spring Boot的应用,以及Spring Boot的一些特性分析。因为一旦你想讲Spring Boot的原理,就必然会回归到Spring这块的内容上。比如小马哥的Spring Boot编程思想着本书,大篇幅的都是在讲Spring Framework。因为Spring Boot的内核还是Spring Framework。

Spring Boot与微服务

接下来,给大家讲讲spring boot与微服务这块的内容。

什么是Spring Cloud

首先,我们要简单了解一下什么是微服务,按照我的理解来说,微服务就是微粒度的服务,它是面向服务架构(SOA)的进一步优化。如果大家不是很好理解,翻译成白话就是

一个业务系统,原本是在一个独立的war包中。现在为了更好的维护和提高性能,把这个war包按照业务纬度拆分成了一个个独立的业务子系统,每个子系统提供该业务领域相关的功能,并暴露API接口。

这些服务彼此之间进行数据交换和通信来实现整个产品的功能。

而这些业务子系统,实际上代表的就是一个服务,那么所谓的微服务,说的是这个服务的粒度。至于服务的粒度什么样才叫微,其实没有一个固定的衡量标准。更多的还是在每个公司具体的业务粒度的把控上。

微服务化遇到的问题

在为服务化之后,会面临很多的问题,比如服务注册、服务路由、负载均衡、服务监控等等。这些问题都需要有相应的技术来解决,这个时候,Spring Cloud就出现了。

简单来说,Spring Cloud 提供了一些可以让开发者快速构建微服务应用的工具,比如配置管理、服务发现、熔断、智能路由等,这些服务可以在任何分布式环境下很好地工作。Spring Cloud 主要 致力于解决如下问题:

  • Distributed/versioned configuration,分布式及版本化配置。
  • Service registration and discovery,服务注册与发现。
  • Routing,服务路由。
  • Service-to-service calls,服务调用。
  • Load balancing,负载均衡。
  • Circuit Breakers,断路器。
  • Global locks,全局锁。
  • Leadership election and cluster state,Leader 选举及集群状态。
  • Distributed messaging,分布式消息。

需要注意的是,Spring Cloud 并不是 Spring 团队全新研发的框架,它只是把一些比较优秀的解决微服务架构中常见问题的开源框架基于 Spring Cloud 规范进行了整合,通过 Spring Boot 这个 框架进行再次封装后屏蔽掉了复杂的配置,给开发者提供良好的开箱即用的微服务开发体验。不难看出,Spring Cloud 其实就是一套规范,而 Spring Cloud Netflix、Spring Cloud Consul、Spring CloudAlibaba 才是 Spring Cloud 规范的实现。

为什么Spring Cloud是基于Spring Boot

那为什么Spring Cloud会采用Spring Boot来作为基础框架呢?原因很简单

  1. Spring Cloud它是关注服务治理领域的解决方案,而服务治理是依托于服务架构之上,所以它仍然需要一个承载框架
  2. Spring Boot 可以简单认为它是一套快速配置Spring应用的脚手架,它可以快速开发单个微服务

在微服务架构下,微服务节点越来越多,需要一套成熟高效的脚手架,而Spring Boot正好可以满足这样的需求,如下图所示。

image-20211124135348046

Spring Boot的四大核心机制

如果一定要基于Spring Boot的特性去说,那么只能去说Spring Boot的四大核心机制,分别是@EnableAutoConfiguration 、 Starter开箱即用组件、Actuator应用监控、Spring Boot CLI 命令行工具。

EnableAutoConfiguration

Starter

告诉Spring Boot需要什么功能,它就能引入需要的库。

Actuator

让你能够深入运行中的Spring Boot应用程序

Spring Boot CLI

Spring Boot CLI 为Spring Cloud 提供了Spring Boot 命令行功能。我们可以通过编写groovy脚本来运行Spring Cloud 组件应用程序。步骤如下、

  • 下载spring-boot-cli

    Spring Boot CLI:repo.spring.io/release/org…

  • 配置环境变量

  • 在控制台spring --version查看CLI版本

  • 使用CLI运行应用。我们可以使用run命令编译和运行Groovy源代码。Spring Boot CLI中包含所有运行Groovy所需要的依赖。

  • 创建一个hello.groovy文件

    @RestController
    class HelloController {
    
    	@GetMapping("/hello")
    	String hello(){
    		return "Hello World";
    	}
    }
    
  • 在控制台执行spring run hello.groovy,如果需要传递参数,比如端口,和JVM参数类似

    spring run hello.groovy -- --server.port=9000
    

Spring Boot的四大核心特性

  • EnableAutoConfiguration

  • Starter

  • Actuator

  • Spring Boot CLI

    Spring Boot CLI 为Spring Cloud 提供了Spring Boot 命令行功能。我们可以通过编写groovy脚本来运行Spring Cloud 组件应用程序。步骤如下、

    • 下载spring-boot-cli

      Spring Boot CLI:repo.spring.io/release/org…

    • 配置环境变量

    • 在控制台spring --version查看CLI版本

    • 使用CLI运行应用。我们可以使用run命令编译和运行Groovy源代码。Spring Boot CLI中包含所有运行Groovy所需要的依赖。

    • 创建一个hello.groovy文件

      @RestController
      class HelloController {
      
      	@GetMapping("/hello")
      	String hello(){
      		return "Hello World";
      	}
      }
      
    • 在控制台执行spring run hello.groovy,如果需要传递参数,比如端口,和JVM参数类似

      spring run hello.groovy -- --server.port=9000
      

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mic带你学架构! 如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注同名微信公众号获取更多技术干货!