使用 Spring Boot 已经两年多了,我从刚开始的抵触,到积极拥抱,再到现在进行若干反思,这中间积累了一些使用心得和一些个人的思考。本文将对 Spring Boot 做一个整体的概述,阅读完文章,你将会了解到下面这些:
- Spring Boot 是什么
- Spring Boot 快速上手
- Spring Boot 的优势
- Spring Boot 的不足
- 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.xml
、applicationContext.xml
、xxx.xml
…… - 文件夹:webapp、static、js…
- 依赖:
spring-web
、spring-context
、spring-beans
、spring-aop
…
使用 Spring Boot 配置的 Spring 项目:
特征:
- 文件:
Application.java
、application.yml
/application.properties
。 - 依赖:
spring-boot-starter
即可。
从图中可以看到,SpingBoot 搭建的应用最明显的就是减少了配置文件的数目,这一点很关键,尤其是在大型应用中,其配置涉及到了存储、安全、业务等方方面面,其配置文件可能会多达几十个,这会使得开发过程变得低效,运维流程的难度变大,工程的质量也不能得到很好的保证。
Spring Boot 的优势
Spring Boot 简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的、产品级别的 Spring 应用。 Spring Boot 为 Spring 平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。
Spring Boot 的特性主要有下面这些:
- 使用 Spring 项目引导页面可以在几秒构建一个项目
- 方便对外输出各种形式的服务,如 REST API、WebSocket、Web、Streaming、Tasks
- 非常简洁的安全策略集成
- 支持关系数据库和非关系数据库
- 支持运行期内嵌容器,如 Tomcat、Jetty
- 强大的开发包,支持热启动
- 自动管理依赖
- 自带应用监控
- 支持各种 IDE,如 IntelliJ IDEA 、NetBeans
Spring Boot 不是为了取代 Spring,Spring Boot 基于 Spring 开发,是为了让人们更容易的使用 Spring。相对于 传统的 Spring 项目,使用 Spring Boot 带来的收益可以总结为:
- 提升了 Sping 应用开发的效率,简单优于复杂,复杂优于繁琐;
- 保证 Sping 应用整体开发风格的统一,代码可读性提升。
Spring Boot 使开发变简单
Spring Boot 使开发变简单主要体现在:
- 在应用开发之初,我们可以通过「Starter」快速搭建好项目的原型;
- Spring Boot 提倡的 约定优于配置 可让我们用较少的代码完成更多的功能。
快速搭建 Spring Boot 应用
快速搭建 Spring Boot 应用的方式有下面三种:
- 前往 http://start.spring.io/,勾选自己所需要的 Spring 模块,如 Spirng、Web、Security、SQL、SpringCloud Config等,选择完成后会生成一个 jar 包,下载后导入 IDE 即可;
- 使用 利用 InteIIiJ IDEA 创建:在 IDEA 中 使用 Spring Initializer 来创建;
- 手动创建:创建一个普通 maven 项目,自己建立 pom 依赖、插件。
这里我们手动来创建一个 Spring Boot 项目:
第一步:添加一个依赖:
1 | <parent> |
第二步:写一个 通过 @Spring BootApplication
注解的 Java 类,内容如下:
1 | @Spring BootApplication |
第三步:没有了,到了这里就可以可以直接运行这个应用了。
上面我创建了一个 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 | <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 | @Spring BootApplication |
@Spring BootConfiguration
继承自@Configuration
,二者功能也一致,标注当前类是配置类;@ComponentScan
注解会自动扫描指定包下的全部标有@Component
注解 的类,并注册成bean,当然包括@Component
下的子注解@Service
、@Repository
、@Controller
。@EnableAutoConfiguration
会根据你添加的jar包来配置你项目的默认配置,比如根据spring-boot-starter-web
,来判断你的项目是否需要添加了 webmvc 和 tomcat,就会自动的帮你配置 web 项目中所需要的默认配置。
JavaConfig 更符合编码习惯
来看一个 mybatis 的配置:
首先是 XML 风格的:
1 | <!-- mybatis.xml --> |
然后是 JavaConfig 风格的:
1 | // SqlSessionFactoryConfig.java 文件 |
我个人的观点是,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 | <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-plugin
、maven-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 | <dependency> |
通过 spring-boot-starter-actuator
提供的 RESTful 接口,我们可在程序应用运行时获取如下信息:
- 应用配置类:可以查看应用在运行期的静态信息:例如自动配置信息、加载的Spring Bean 信息、yml 文件配置信息、环境信息、请求映射信息;
- 度量指标类:主要是运行期的动态信息,例如堆栈、线程活动的快照、一些健康指标、metrics 信息等;
- 操作控制类:主要是指 shutdown,用户可以发送一个请求将应用的监控功能关闭。
当然,除了这些内置的监控点,用户可以根据自己的实际应用,定义一些比较关心的指标,在运行期进行监控。
Spring Boot 的不足
根据我使用 Spring Boot 的经验和期间踩过的一些坑来看,Spring Boot 的不足主要是由「Starter」引起的(真是一把双刃剑啊):
- Spring Boot 通过「Starter」规范了依赖,但与此同时也会引入一些冗余的 Jar,所以包的体积会变大
- 「Starter」引入的一些依赖会造成包冲突,我遇到过包括有:
- Spring Boot 间接引入的
hibernate-validator
让kubernetes-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 ?
随着 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 应用的步骤主要有下面这些:
- 将 xml 配置的 Bean 通过 JavaConfig 的方式进行配置,这一点主要通过注解来实现,如
@Bean
、@Service
、@Component
、@Configuration
等; - 将 bean 的配置(setBean)用
@Autowired
注解来实现; - 将 配置通过
application.yml
或者application.properties
实现; - 打包部署方式 通过
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一样流行。