使用Spring Boot快速搭建微服务

文章目录
  1. 1. Spring Boot 简介
  2. 2. Spring Boot 的优势
    1. 2.1. Spring Boot 使开发变简单
      1. 2.1.1. 快速搭建 Spring Boot 应用
      2. 2.1.2. 约定优于配置
        1. 2.1.2.1. @Spring BootApplication 注解
        2. 2.1.2.2. JavaConfig 更符合编码习惯
    2. 2.2. Spring Boot 使部署变简单
    3. 2.3. Spring Boot 使监控变简单
  3. 3. Spring Boot 的不足
  4. 4. Spring Boot 的实践经验
    1. 4.1. 我们是否需要了解并使用 Spring Boot ?
    2. 4.2. 传统 Spring 项目转为 Spring Boot 应用
    3. 4.3. Spring、Spring Boot、Spring Cloud

使用 Spring Boot 已经两年多了,我从刚开始的抵触,到积极拥抱,再到现在进行若干反思,这中间积累了一些使用心得和一些个人的思考。本文将对 Spring Boot 做一个整体的概述,阅读完文章,你将会了解到下面这些:

  1. Spring Boot 是什么
  2. Spring Boot 快速上手
  3. Spring Boot 的优势
  4. Spring Boot 的不足
  5. Spring Boot 实践经验

注意:下面的介绍中,将不区分 应用服务(当然它们本质上是有区别的),都指的是一个 Spring Boot 项目,此外,除非特别说明,下面的包依赖管理工具均指的是 maven。

Spring Boot 简介

Spring 项目是在 2004 年 03 月,1.0 版发布的,在2017 年 09 月,发布了Spring 5.0,事实上 Spring 已经成为了 企业 Java 应用的开发标准。随着 Spirng 对其他开源软件的不断集成(ORM、Message…),很多配置文件被引入(通常是通过 XML 类配置),太多的配置非常难以理解,并容易出错,到了后来人们甚至称 Spring 为配置地狱。

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置,带来免XML配置的开发体验。2014年4月,Spring Boot 1.0.0 发布,基于 Sring 3.x。

SpirngBoot 究竟带来了哪些改变?在正式介绍之前,我们先通过两张图,来看一下传统的 Spring 项目 和 Spring Boot 项目的区别:

传统的 xml 配置的 Spring 项目:

特征:

  • 文件:web.xmlapplicationContext.xmlxxx.xml……
  • 文件夹:webapp、static、js…
  • 依赖:spring-webspring-contextspring-beansspring-aop

使用 Spring Boot 配置的 Spring 项目:

特征:

  • 文件:Application.javaapplication.yml /application.properties
  • 依赖:spring-boot-starter 即可。

从图中可以看到,SpingBoot 搭建的应用最明显的就是减少了配置文件的数目,这一点很关键,尤其是在大型应用中,其配置涉及到了存储、安全、业务等方方面面,其配置文件可能会多达几十个,这会使得开发过程变得低效,运维流程的难度变大,工程的质量也不能得到很好的保证。

Spring Boot 的优势

Spring Boot 简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的、产品级别的 Spring 应用。 Spring Boot 为 Spring 平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。

Spring Boot 的特性主要有下面这些:

  1. 使用 Spring 项目引导页面可以在几秒构建一个项目
  2. 方便对外输出各种形式的服务,如 REST API、WebSocket、Web、Streaming、Tasks
  3. 非常简洁的安全策略集成
  4. 支持关系数据库和非关系数据库
  5. 支持运行期内嵌容器,如 Tomcat、Jetty
  6. 强大的开发包,支持热启动
  7. 自动管理依赖
  8. 自带应用监控
  9. 支持各种 IDE,如 IntelliJ IDEA 、NetBeans

Spring Boot 不是为了取代 Spring,Spring Boot 基于 Spring 开发,是为了让人们更容易的使用 Spring。相对于 传统的 Spring 项目,使用 Spring Boot 带来的收益可以总结为:

  1. 提升了 Sping 应用开发的效率,简单优于复杂,复杂优于繁琐;
  2. 保证 Sping 应用整体开发风格的统一,代码可读性提升。

Spring Boot 使开发变简单

Spring Boot 使开发变简单主要体现在:

  1. 在应用开发之初,我们可以通过「Starter」快速搭建好项目的原型;
  2. Spring Boot 提倡的 约定优于配置 可让我们用较少的代码完成更多的功能。

快速搭建 Spring Boot 应用

快速搭建 Spring Boot 应用的方式有下面三种:

  1. 前往 http://start.spring.io/,勾选自己所需要的 Spring 模块,如 Spirng、Web、Security、SQL、SpringCloud Config等,选择完成后会生成一个 jar 包,下载后导入 IDE 即可;
  2. 使用 利用 InteIIiJ IDEA 创建:在 IDEA 中 使用 Spring Initializer 来创建;
  3. 手动创建:创建一个普通 maven 项目,自己建立 pom 依赖、插件。

这里我们手动来创建一个 Spring Boot 项目:

第一步:添加一个依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/>
</parent>

<dependencies>
<!-- dependencies中添加web支持的starter pom,这样就添加了web的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

第二步:写一个 通过 @Spring BootApplication 注解的 Java 类,内容如下:

1
2
3
4
5
6
@Spring BootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}

第三步:没有了,到了这里就可以可以直接运行这个应用了。

上面我创建了一个 Spring Web 应用。spring-boot-starter-web 包含了 Spring Web 的相关依赖(包括 Json 支持的 Jackson 和数据校验的 Hibernate Validator)和一个内置的 Tomcat 容器,这使得在开发阶段可以直接通过 main 方法或是 JAR 包独立运行一个 WEB 项目。而在部署阶段也可以打成 WAR 包放到生产环境运行。

同时,我们也看到了,Spring Boot 通过「Starter」解决了我们应用开发过程中的关键环节:依赖管理。依赖管理是任何复杂项目的关键部分。以手动的方式来实现依赖管理不太现实,你得花更多时间,同时你在项目的其他重要方面能付出的时间就会变得越少。「Starter」使得 pom 可管理性增强,减少了项目的整体配置时间。

Spring Boot 提供了很多「Starter」,如我们需要测试库,则引入 spring-boot-starter-test 即可:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

完整的 「Starter」 列表请参考 Spring Boot application starters

约定优于配置

为什么使用 Spring Boot,因为程序员总是希望用更少的代码做更多的事情,在 Sring 后者 J2EE 应用的开发中,所谓的配置,都有哪些呢?

  • code 中用到的各种属性,如数据源地址、第三方服务的 URL 等;
  • 监听器(listener)、过滤器(filter)、(interceptor)
  • Spring MVC 配置,如 ViewResolver 配置,路径映射(Mapping)等;
  • Spring 中的 Bean 的管理、使用;
  • ……

传统基于 XML 的配置,明显的两个痛点就是:

  • 配置繁琐
  • 风格不统一

如下图所示的 Web 服务,我们需要配置 Spring Spring MVC、data source、ehcache、log、properties 等。不同的开发者有不同的配置风格,举例:

  • 有人偏向按照类别把 Bean 放在不同的 xml 文件,如安全,缓存,RPC 服务等,但也有人会根据业务属性划分 Bean;
  • 有人喜欢把所有的配置(properties)放在一个文件中,有人喜欢随用随配;
  • 配置文件命名风格千奇百怪;
  • ……

Spring Boot 让配置变简单,主要体现在其核心思想:约定优于配置 上。那么什么是约定优于配置呢?约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。约定优于配置 在当前的软件开发流程中,已经逐渐成为了一种趋势,如 maven 中的版本规范:<majorversion>.<minor version>.<incremental version>-<qualifier>

约定优于配置让我们仅需关心应用中不符约定的部分。例如,如果模型中有个名为 User 的类,那么数据库中对应的表就会默认命名为 user。只有在偏离这一约定时,例如将该表命名为 ”user_info”,才需写有关这个名字的配置。再如,在基于 xml 配置的项目中,properties 会散落在各处,这会带来维护上的困难,在 Spring Boot 中,框架约定了 properties 只能以 位于 application.properties 文件 或者 类似 application-xxx.properties 的变体文件中。

事实上,在 Spring Boot 出现之前,Spring 每个新版本的推出都以减少配置作为自己的主要目标,例如

  • 注解声明 Bean;
  • Java 配置代替 xml 配置;
  • 越来越多的注解使用。

下面举两个约定大于配置的例子,来展示一下这种思想给我们带来的开发上的便捷性。

@Spring BootApplication 注解

@Spring BootApplication 是一个复合注解,包括 @ComponentScan,和 @Spring BootConfiguration@EnableAutoConfiguration

1
2
3
4
5
6
@Spring BootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
  • @Spring BootConfiguration 继承自@Configuration,二者功能也一致,标注当前类是配置类;
  • @ComponentScan 注解会自动扫描指定包下的全部标有 @Component 注解 的类,并注册成bean,当然包括 @Component 下的子注解@Service@Repository@Controller
  • @EnableAutoConfiguration 会根据你添加的jar包来配置你项目的默认配置,比如根据 spring-boot-starter-web ,来判断你的项目是否需要添加了 webmvc 和 tomcat,就会自动的帮你配置 web 项目中所需要的默认配置。
JavaConfig 更符合编码习惯

来看一个 mybatis 的配置:

首先是 XML 风格的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- mybatis.xml -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--<property name="typeAliasesPackage" value="domanin-path"/>-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 配置扫描Mapper XML的位置 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>

<!-- mybatis-config.xml -->
<configuration>
<settings>
<setting name="cacheEnabled" value="false"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
</configuration>

然后是 JavaConfig 风格的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// SqlSessionFactoryConfig.java 文件
@Configuration
public class SqlSessionFactoryConfig implements TransactionManagementConfigurer {
@Bean(name ="sqlSessionFactory")
public SqlSessionFactory harborSqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setConfigurationProperties(sqlSessionConfig());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("domanin-path");
return sqlSessionFactoryBean.getObject();
}

public Properties sqlSessionConfig() {
Properties properties = new Properties();
properties.setProperty("cacheEnabled", Constant.TRUE);
properties.setProperty("lazyLoadingEnabled", Constant.TRUE);
properties.setProperty("aggressiveLazyLoading", Constant.FALSE);
return properties;
}
}

我个人的观点是,Java Config 风格的配置更符合程序员的编码习惯。事实上,这些 XML 的配置,在使用时也是被解析成了 DOM 节点,最终变成了类似代码的结构。

Spring Boot 使部署变简单

想象一下在 Spring Boot 出来以前,我们怎么打包部署我们的应用?

  • 打包项目所需要的依赖(mvn package);
  • 准备 web 容器环境(Tomcat、Weblogic…);
  • 解压 war / jar 包;
  • 指定 classpath,启动应用;

使用 Spring Boot 后,我们只需要两步:

  • mvn package 打包应用;
  • java -jar xxx.jar,应用启动。

如此便捷的打包部署过程,只需要在 maven 的 build 环节使用 spring-boot-maven-plugin 这个插件即可实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<build>
<finalName>spring-boot-demo</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<addResources>true</addResources>
<executable>true</executable>
<fork>true</fork>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>path.to.Application</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

执行 mvn package 后,我们就 得到了 spring-boot-demo.jar 这个文件,通过 执行 java -jar spring-boot-demo.jar 这一条命令,我们就可以将这个应用运行起来。同时,我们也注意到,使用 Spring Boot 打包得到的spring-boot-demo.jar的 是一个 flat-jar 即它包含了应用运行时所需要的第三方库,spring-boot-demo.jar 的结构如下图所示:

当然不使用 Spring Boot 也可以将应用打包成 flat-jar ,如maven-shade-pluginmaven-assembly-plugin 等插件。不过通过这样的方式打包得到的应用还是需要我们自己准备一个 Web 容器。

总结一下:使用 Spring Boot 可以加快并简化我们打包部署应用的流程,我们只需要提供 JRE,即对运行时环境的基本要求降低了。

Spring Boot 使监控变简单

Spring Boot 提供了 spring-boot-starter-actuator 这个模块,用于暴露 Spring Boot 自身信息。spring-boot-starter-actuator 提供了对于应用系统的自省和监控的集成功能,可以查看应用配置的详细信息,例如自动化配置信息、创建的 Spring beans 以及一些环境属性等。

spring-boot-starter-actuator 的使用非常简单:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

通过 spring-boot-starter-actuator 提供的 RESTful 接口,我们可在程序应用运行时获取如下信息:

  1. 应用配置类:可以查看应用在运行期的静态信息:例如自动配置信息、加载的Spring Bean 信息、yml 文件配置信息、环境信息、请求映射信息;
  2. 度量指标类:主要是运行期的动态信息,例如堆栈、线程活动的快照、一些健康指标、metrics 信息等;
  3. 操作控制类:主要是指 shutdown,用户可以发送一个请求将应用的监控功能关闭。

当然,除了这些内置的监控点,用户可以根据自己的实际应用,定义一些比较关心的指标,在运行期进行监控。

Spring Boot 的不足

根据我使用 Spring Boot 的经验和期间踩过的一些坑来看,Spring Boot 的不足主要是由「Starter」引起的(真是一把双刃剑啊):

  1. Spring Boot 通过「Starter」规范了依赖,但与此同时也会引入一些冗余的 Jar,所以包的体积会变大
  2. 「Starter」引入的一些依赖会造成包冲突,我遇到过包括有:
    • Spring Boot 间接引入的 hibernate-validatorkubernetes-client 的读写性能变差;
    • Spring Boot 1.5.7-RELEASE 版本中定义的 elasticsearch 版本为 2.x.x,在 Spring Boot 2.0 中,elastisearch 版本变为了 5.6.10。

此外,Spring Boot 作为一种新的框架,不可避免的会带来一定的学习成本(虽然我看来其实很小),约定优于配置会造成开发者在学习之初的学习曲线陡峭,但其实如果你初步上手 Spring Boot 后,开发效率会越来越高。

Spring Boot 的实践经验

我们是否需要了解并使用 Spring Boot ?

随着 Spring Boot 2.0 在 2018年发布,Spring Boot 的关注度达到了顶峰,事实上,Spring 官方已经越来越重视 Spring Boot 的发展,Spring Boot 作为公司最顶级的项目来推广,已经被放到了官网上第一的位置。下图是 Google Trend 统计的「Spring Boot」在近5年的热度变化:

不知道什么时候起,行业里一些开发人员愿意相信,使用复杂的软件就意味着采用了高深的技术;使用了大量的配置,就意味着软件有着很多比较强大的功能。在产品设计的时候有一个理念就是让产品操作足够的傻瓜化,假设用户是一个智商并不高的群体,却可以使他很容易的学会使用其产品,将此特性做为产品设计的一项标准之一。

其实我们的开源软件也是一款产品,繁琐并不意味着功能强大,反而有可能是设计不够合理;简洁也并不意味着简单,很有可能它只是将众多复杂的功能进行了封装,让我们在使用的时候足够的简单。好的产品如此,好的开源软件也应该如此,Spring Boot 的出现就是让编程变得更简单一些。

传统 Spring 项目转为 Spring Boot 应用

如果现有的应用已经非常成熟了,那么保持现状吧,引入 Spring Boot 后可能会带来一些潜在的风险(主要是包依赖引起),同时投入产出比也不高。:

如果应用处于起步阶段,那么强烈建议将非 Spring Boot 应用 转变为 Spring Boot 应用,这种转变带来的收益将在应用发展后期越发地明显。将基于 xml 配置的 Spring 应用 转为 Spring Boot 应用的步骤主要有下面这些:

  1. 将 xml 配置的 Bean 通过 JavaConfig 的方式进行配置,这一点主要通过注解来实现,如 @Bean@Service@Component@Configuration 等;
  2. 将 bean 的配置(setBean)用 @Autowired 注解来实现;
  3. 将 配置通过 application.yml 或者 application.properties 实现;
  4. 打包部署方式 通过 spring-boot-maven-plugin 模块实现。

Spring、Spring Boot、Spring Cloud

需要再次强调的是:Spring Boot 不是新的技术,Spring Boot 是 Spring 的一套快速配置脚手架,可以基于 Spring Boot 快速开发单个微服务。

Spring Cloud 基于 Spring Boot,为微服务体系开发中的架构问题,提供了一整套的解决方案:服务注册与发现,服务消费,服务保护与熔断,网关,分布式调用追踪,分布式配置管理等,和 Spring Cloud 类似的有阿里的 Dubbo(准确来讲 Dubbo 类似于 Spring Cloud 的一个子集)、Kubernetes (和Spring Cloud 有交集)等。

Spring Cloud 全家桶社区支持强大,更新非常快,所以开发效率高。Spring Cloud 是快速实现整体微服务架构的最佳解决方案,也是一个趋势,未来一两年可能就会像 Spring一样流行。