鸟哥的Spring Boot私房菜

1、写在前面

两个月之前有朋友问鸟哥了解Spring Boot吗?鸟哥还表示没用过Spring Boot,只是大概知道它可以简化Spring开发。但是最近由于工作中要用到Neo4j,官网给的例子又是基于Spring Boot整合Neo4j开发的,因此鸟哥决定花时间学习一下这个当前较为流行的开发框架。
在使用了Spring Boot之后相信很多朋友也跟鸟哥一样爱上了这个神奇的框架,它让我们更加专注于业务代码上,而不用在各种繁杂的配置或者样板工作上花费多余的时间。
经历了“Struts+Hibernate+Spring”时代到“Spring MVC+MyBatis”时代,再到现在的Spring Boot,开发人员的工作量明显的大大的减少了,因为框架已经帮我们做了大量繁杂的工作。但是在享受技术进步带来的便利的同时,鸟哥建议大家还是要花时间认真学习Spring的原理(如果时间允许最好学习一下其源代码 ^ _ ^)

2、再忆Spring

Spring诞生时是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入面向切面编程,用简单的Java对象(Plain Old Java Object, POJO)实现了EJB的功能。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。 Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。尽管如此,我们依旧没能逃脱配置的魔爪。
假设你受命用Spring开发一个简单的Hello World Web应用程序。你该做什么?我能想到一些基本的需要。

  • 一个项目结构,其中有一个包含必要依赖的Maven或者Gradle构建文件,最起码要有Spring
    MVC和Servlet API这些依赖。
  • 一个web.xml文件(或者一个WebApplicationInitializer实现),其中声明了Spring的DispatcherServlet。
  • 一个启用了Spring MVC的Spring配置。
  • 一个控制器类,以“ Hello World”响应HTTP请求。
  • 一个用于部署应用程序的Web应用服务器,比如Tomcat。

最让人难以接受的是,这份清单里只有一个东西是和Hello World功能相关的,即控制器,剩下的都是Spring开发的Web应用程序必需的通用样板

3、Spring Boot精要

既然所有Spring Web应用程序都要用到它们,那为什么还要你来提供这些东西呢?Spring Boot会搞定执行应用程序所需的各种后勤工作,你只要搞定应用程序的业务代码就好。
Spring Boot将很多魔法带入了Spring应用程序的开发之中,其中最重要的是以下四个核心:

  • 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。
  • 自动配置:针对很多Spring应用程序常见的应用功能, Spring Boot能自动提供相关配置。
  • 命令行界面:这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建。
  • Actuator:让你能够深入运行中的Spring Boot应用程序,一探究竟。

在这四个核心中,目前我们最感兴趣的就是起步依赖和自动配置的内容,因此下面将对这两个部分进行更多详细的介绍。其他两个模块的内容在后续的版本中在找时间进行介绍。

3.1 起步依赖

要理解Spring Boot起步依赖带来的好处,先让我们假设它们尚不存在。如果没用Spring Boot的话,你会向项目里添加哪些依赖呢?它们的Group和Artifact是什么?你需要哪个版本?哪个版本不会和项目中的其他依赖发生冲突?
这是一件极具挑战的事情,你需要多次尝试以保证项目的依赖版本之间不存在冲突,这可能要花费你不少的时间,甚至这个过程中你要经历数次失败。
值得高兴的是Spring Boot已经帮我们做了这些工作,Spring Boot通过起步依赖为项目的依赖管理提供帮助。起步依赖其实就是特殊的Maven依赖和Gradle依赖,利用了传递依赖解析,把常用库聚合在一起,组成了几个为特定功能而定制的依赖
比起减少依赖数量,起步依赖还引入了一些微妙的变化。向项目中添加了Web起步依赖,实际上指定了应用程序所需的一类功能。因为应用是Web应用程序,所以加入了Web起步依赖。与之类似,如果应用程序要用到JPA持久化,那么就可以加入jpa起步依赖。简而言之,你不再需要考虑支持某种功能要用什么库了,引入相关起步依赖就行
此外, Spring Boot的起步依赖还把你从“需要这些库的哪些版本”这个问题里解放了出来。起步依赖引入的库的版本都是经过测试的,因此你可以完全放心,它们之间不会出现不兼容的情况

3.1.1 指定基于功能的依赖

Spring Boot通过提供众多起步依赖降低项目依赖的复杂度。起步依赖本质上是一个Maven项目对象模型(Project Object Model, POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。很多起步依赖的命名都暗示了它们提供的某种或某类功能。
例如,我们想要创建一个web项目,用Thymeleaf来定义Web视图,用Spring Data JPA来把阅读列表持久化到数据库里,姑且先用嵌入式的H2数据库,最后还要添加测试依赖test。在Maven项目的pom.xml文件里看起来是这样的:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

在Gradle的构建文件里看起来是这样的:

dependencies {
  compile "org.springframework.boot:spring-boot-starter-web"
  compile "org.springframework.boot:spring-boot-starter-thymeleaf"
  compile "org.springframework.boot:spring-boot-starter-data-jpa"
  compile "com.h2database:h2"
  testCompile("org.springframework.boot:spring-boot-starter-test")
}

3.1.2 覆盖起步依赖引入的传递依赖

你也可以通过构建工具中的功能,选择性地覆盖它们引入的传递依赖的版本号,排除传递依赖,当然还可以为那些Spring Boot起步依赖没有涵盖的库指定依赖。
以Spring Boot的Web起步依赖为例,它传递依赖了Jackson JSON库。如果你正在构建一个生产或消费JSON资源表述的REST服务,那它会很有用。但是,要构建传统的面向人类用户的Web应用程序,你可能用不上Jackson。虽然把它加进来也不会有什么坏处,但排除掉它的传递依赖,可以为你的项目瘦身。
在Maven里,可以用<exclusions>元素来排除传递依赖。下面这个引入Spring Boot的build.gradle的<dependency>增加了<exclusions>元素去除Jackson:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <groupId>com.fasterxml.jackson.core</groupId>
    </exclusion>
  </exclusions>
</dependency>

另一方面,也许项目需要Jackson,但你需要用另一个版本的Jackson来进行构建,而不是Web起步依赖里的那个。假设Web起步依赖引用了Jackson 2.3.4,但你需要使用2.4.3①。在Maven里,你可以直接在pom.xml中表达诉求,就像这样:

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

如果在用Gradle,你可以这样排除传递依赖:

compile("org.springframework.boot:spring-boot-starter-web") {
  exclude group: 'com.fasterxml.jackson.core'
}

与之类似,可以在build.gradle文件里指明你要的Jackson的版本:

compile("com.fasterxml.jackson.core:jackson-databind:2.4.3")

3.2 自动配置

Spring Boot会为常见配置场景进行自动配置。如果Spring Boot在应用程序的Classpath里发 现 H2 数 据 库 的 库 , 那 么 它 就 自 动 配 置 一 个 嵌 入 式 H2 数 据 库 。 如 果 在 Classpath 里 发 现JdbcTemplate,那么它还会为你配置一个JdbcTemplate的Bean。你无需操心那些Bean的配置,Spring Boot会做好准备,随时都能将其注入到你的Bean里。
简而言之, Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。举几个例子,下面这些情况都是Spring Boot的自动配置要考虑的。

  • Spring的JdbcTemplate是不是在Classpath里?如果是,并且有DataSource的Bean,则自动配置一个JdbcTemplate的Bean。
  • Thymeleaf是不是在Classpath里?如果是,则配置Thymeleaf的模板解析器、视图解析器以及模板引擎。
  • Spring Security是不是在Classpath里?如果是,则进行一个非常基本的Web安全设置。

每当应用程序启动的时候, Spring Boot的自动配置都要做将近200个这样的决定,涵盖安全、集成、持久化、 Web开发等诸多方面。所有这些自动配置就是为了尽量不让你自己写配置。

3.2.1 原理分析

在向应用程序加入Spring Boot时,有个名为spring-boot-autoconfigure的JAR文件,其中包含了很多配置类。每个配置类都在应用程序的Classpath里,都有机会为应用程序的配置添砖加瓦。这些配置类里有用于Thymeleaf的配置,有用于Spring Data JPA的配置,有用于Spiring MVC的配置,还有很多其他东西的配置,你可以自己选择是否在Spring应用程序里使用它们。
所有这些配置如此与众不同,原因在于它们利用了Spring的条件化配置,这是Spring 4.0引入的新特性。条件化配置允许配置存在于应用程序中,但在满足某些特定条件之前都忽略这个配置。
Spring Boot运用条件化配置的方法是,定义多个特殊的条件化注解,并将它们用到配置类上。表2-1列出了Spring Boot提供的条件化注解。

条件化注解

我们可以看一下DataSourceAutoConfiguration里的这个片段(这是Spring Boot自动配置库的一部分):

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
  ...
}

如你所见, DataSourceAutoConfiguration添加了@Configuration注解,它从其他配置类里导入了一些额外配置,还自己定义了一些Bean。最重要的是, DataSourceAutoConfiguration上添加了@ConditionalOnClass注解,要求Classpath里必须要有DataSource和EmbeddedDatabaseType。如果它们不存在,条件就不成立,DataSourceAutoConfiguration提供的配置都会被忽略掉。

3.2.2 通过属性文件外置配置

Spring Boot自动配置的Bean提供了300多个用于微调的属性。当你调整设置时,只要在环境变量、 Java系统属性、 JNDI(Java Naming and Directory Interface)、命令行参数或者属性文件里进行指定就好了。
实际上, Spring Boot应用程序有多种设置途径。 Spring Boot能从多种属性源获得属性,包括如下几处:
(1) 命令行参数
(2) java:comp/env里的JNDI属性
(3) JVM系统属性
(4) 操作系统环境变量
(5) 随机生成的带random.*前缀的属性(在设置其他属性时,可以引用它们,比如${random.long})
(6) 应用程序以外的application.properties或者appliaction.yml文件
(7) 打包在应用程序内的application.properties或者appliaction.yml文件
(8) 通过@PropertySource标注的属性源
(9) 默认属性

这个列表按照优先级排序,也就是说,任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性。例如,命令行参数会覆盖其他属性源里的属性。

application.properties和application.yml文件能放在以下四个位置。
(1) 外置,在相对于应用程序运行目录的/config子目录里。
(2) 外置,在应用程序运行的目录里。
(3) 内置,在config包内。
(4) 内置,在Classpath根目录。

同样,这个列表按照优先级排序。也就是说, /config子目录里的application.properties会覆盖应用程序Classpath里的application.properties中的相同属性。此外,如果你在同一优先级位置同时有application.properties和application.yml,那么application.yml里的属性会覆盖application.properties里的属性。

3.2.3 使用 Profile 进行配置

当应用程序需要部署到不同的运行环境时,一些配置细节通常会有所不同。比如,数据库连接的细节在开发环境下和测试环境下就会不一样,在生产环境下又不一样。 Spring Framework从Spring 3.1开始支持基于Profile的配置。 Profile是一种条件化配置,基于运行时激活的Profile,会使用或者忽略不同的Bean或配置类。
例如,我们就能为SecurityConfig加上@Profile注解:

@Profile("production")
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  ...
}

但由于Spring Boot的自动配置替你做了太多的事情,要找到一个能放置@Profile的地方还真不怎么方便。幸运的是, Spring Boot支持为application.properties和application.yml里的属性配置Profile。

① 使用特定于Profile的属性文件
如果你正在使用application.properties,可以创建额外的属性文件,遵循application-{profile}.properties这种命名格式,这样就能提供特定于Profile的属性了。
在日志这个例子里,开发环境的配置可以放在名为application-development.properties的文件里,配置包含日志级别和输出到控制台:

logging.level.root=DEBUG

对于生产环境, application-production.properties会将日志级别设置为WARN或更高级别,并将日志写入日志文件:

logging.path=/var/logs/
logging.file=BookWorm.log
logging.level.root=WARN

与此同时,那些并不特定于哪个Profile或者保持默认值(以防万一有哪个特定于Profile的配置不指定这个值)的属性,可以继续放在application.properties里.

② 使用多Profile YAML文件进行配置
如果使用YAML来配置属性,则可以遵循与配置文件相同的命名规范,即创建application-{profile}.yml这样的YAML文件,并将与Profile无关的属性继续放在application.yml里。但既然用了YAML,你就可以把所有Profile的配置属性都放在一个application.yml文件里。举例来说,我们可以像下面这样声明日志配置:

logging:
  level:
    root: INFO
---
spring:
  profiles: development
logging:
  level:
    root: DEBUG
---
spring:
  profiles: production
logging:
  path: /tmp/
  file: BookWorm.log
  level:
    root: WARN

4、小结

这篇博文中主要介绍了Spring Boot四个核心内容中的起步依赖和自动配置的内容,因为鸟哥认为这两个部分的内容相对比较重要。尽管使用Spring Boot开发给我们带来了诸多便利,但是其本质上还是Spring,想要用好Spring Boot,我们仍然需要去深入地学习Spring。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容