1贰军、寫在前面
兩個(gè)月之前有朋友問(wèn)鳥(niǎo)哥了解Spring Boot嗎殿漠?鳥(niǎo)哥還表示沒(méi)用過(guò)Spring Boot,只是大概知道它可以簡(jiǎn)化Spring開(kāi)發(fā)驯击。但是最近由于工作中要用到Neo4j拢肆,官網(wǎng)給的例子又是基于Spring Boot整合Neo4j開(kāi)發(fā)的减响,因此鳥(niǎo)哥決定花時(shí)間學(xué)習(xí)一下這個(gè)當(dāng)前較為流行的開(kāi)發(fā)框架。
在使用了Spring Boot之后相信很多朋友也跟鳥(niǎo)哥一樣愛(ài)上了這個(gè)神奇的框架郭怪,它讓我們更加專注于業(yè)務(wù)代碼上支示,而不用在各種繁雜的配置或者樣板工作上花費(fèi)多余的時(shí)間。
經(jīng)歷了“Struts+Hibernate+Spring”時(shí)代到“Spring MVC+MyBatis”時(shí)代鄙才,再到現(xiàn)在的Spring Boot颂鸿,開(kāi)發(fā)人員的工作量明顯的大大的減少了,因?yàn)榭蚣芤呀?jīng)幫我們做了大量繁雜的工作攒庵。但是在享受技術(shù)進(jìn)步帶來(lái)的便利的同時(shí)嘴纺,鳥(niǎo)哥建議大家還是要花時(shí)間認(rèn)真學(xué)習(xí)Spring的原理(如果時(shí)間允許最好學(xué)習(xí)一下其源代碼 ^ _ ^)败晴。
2、再憶Spring
Spring誕生時(shí)是Java企業(yè)版(Java Enterprise Edition栽渴,JEE尖坤,也稱J2EE)的輕量級(jí)代替品。無(wú)需開(kāi)發(fā)重量級(jí)的Enterprise JavaBean(EJB)闲擦,Spring為企業(yè)級(jí)Java開(kāi)發(fā)提供了一種相對(duì)簡(jiǎn)單的方法慢味,通過(guò)依賴注入和面向切面編程,用簡(jiǎn)單的Java對(duì)象(Plain Old Java Object佛致, POJO)實(shí)現(xiàn)了EJB的功能贮缕。
雖然Spring的組件代碼是輕量級(jí)的,但它的配置卻是重量級(jí)的俺榆。Spring 2.5引入了基于注解的組件掃描感昼,這消除了大量針對(duì)應(yīng)用程序自身組件的顯式XML配置。 Spring 3.0引入了基于Java的配置罐脊,這是一種類型安全的可重構(gòu)配置方式定嗓,可以代替XML。盡管如此萍桌,我們依舊沒(méi)能逃脫配置的魔爪宵溅。
假設(shè)你受命用Spring開(kāi)發(fā)一個(gè)簡(jiǎn)單的Hello World Web應(yīng)用程序。你該做什么上炎?我能想到一些基本的需要恃逻。
- 一個(gè)項(xiàng)目結(jié)構(gòu),其中有一個(gè)包含必要依賴的Maven或者Gradle構(gòu)建文件藕施,最起碼要有Spring
MVC和Servlet API這些依賴寇损。 - 一個(gè)web.xml文件(或者一個(gè)WebApplicationInitializer實(shí)現(xiàn)),其中聲明了Spring的DispatcherServlet裳食。
- 一個(gè)啟用了Spring MVC的Spring配置矛市。
- 一個(gè)控制器類,以“ Hello World”響應(yīng)HTTP請(qǐng)求诲祸。
- 一個(gè)用于部署應(yīng)用程序的Web應(yīng)用服務(wù)器浊吏,比如Tomcat。
最讓人難以接受的是救氯,這份清單里只有一個(gè)東西是和Hello World功能相關(guān)的找田,即控制器,剩下的都是Spring開(kāi)發(fā)的Web應(yīng)用程序必需的通用樣板径密。
3午阵、Spring Boot精要
既然所有Spring Web應(yīng)用程序都要用到它們,那為什么還要你來(lái)提供這些東西呢?Spring Boot會(huì)搞定執(zhí)行應(yīng)用程序所需的各種后勤工作底桂,你只要搞定應(yīng)用程序的業(yè)務(wù)代碼就好植袍。
Spring Boot將很多魔法帶入了Spring應(yīng)用程序的開(kāi)發(fā)之中,其中最重要的是以下四個(gè)核心:
- 起步依賴:告訴Spring Boot需要什么功能籽懦,它就能引入需要的庫(kù)于个。
- 自動(dòng)配置:針對(duì)很多Spring應(yīng)用程序常見(jiàn)的應(yīng)用功能, Spring Boot能自動(dòng)提供相關(guān)配置暮顺。
- 命令行界面:這是Spring Boot的可選特性厅篓,借此你只需寫代碼就能完成完整的應(yīng)用程序,無(wú)需傳統(tǒng)項(xiàng)目構(gòu)建捶码。
- Actuator:讓你能夠深入運(yùn)行中的Spring Boot應(yīng)用程序羽氮,一探究竟。
在這四個(gè)核心中惫恼,目前我們最感興趣的就是起步依賴和自動(dòng)配置的內(nèi)容档押,因此下面將對(duì)這兩個(gè)部分進(jìn)行更多詳細(xì)的介紹。其他兩個(gè)模塊的內(nèi)容在后續(xù)的版本中在找時(shí)間進(jìn)行介紹祈纯。
3.1 起步依賴
要理解Spring Boot起步依賴帶來(lái)的好處令宿,先讓我們假設(shè)它們尚不存在。如果沒(méi)用Spring Boot的話腕窥,你會(huì)向項(xiàng)目里添加哪些依賴呢粒没?它們的Group和Artifact是什么?你需要哪個(gè)版本簇爆?哪個(gè)版本不會(huì)和項(xiàng)目中的其他依賴發(fā)生沖突癞松?
這是一件極具挑戰(zhàn)的事情,你需要多次嘗試以保證項(xiàng)目的依賴版本之間不存在沖突入蛆,這可能要花費(fèi)你不少的時(shí)間拦惋,甚至這個(gè)過(guò)程中你要經(jīng)歷數(shù)次失敗。
值得高興的是Spring Boot已經(jīng)幫我們做了這些工作安寺,Spring Boot通過(guò)起步依賴為項(xiàng)目的依賴管理提供幫助。起步依賴其實(shí)就是特殊的Maven依賴和Gradle依賴首尼,利用了傳遞依賴解析挑庶,把常用庫(kù)聚合在一起,組成了幾個(gè)為特定功能而定制的依賴软能。
比起減少依賴數(shù)量迎捺,起步依賴還引入了一些微妙的變化。向項(xiàng)目中添加了Web起步依賴查排,實(shí)際上指定了應(yīng)用程序所需的一類功能凳枝。因?yàn)閼?yīng)用是Web應(yīng)用程序,所以加入了Web起步依賴。與之類似岖瑰,如果應(yīng)用程序要用到JPA持久化叛买,那么就可以加入jpa起步依賴。簡(jiǎn)而言之蹋订,你不再需要考慮支持某種功能要用什么庫(kù)了率挣,引入相關(guān)起步依賴就行。
此外露戒, Spring Boot的起步依賴還把你從“需要這些庫(kù)的哪些版本”這個(gè)問(wèn)題里解放了出來(lái)椒功。起步依賴引入的庫(kù)的版本都是經(jīng)過(guò)測(cè)試的,因此你可以完全放心智什,它們之間不會(huì)出現(xiàn)不兼容的情況动漾。
3.1.1 指定基于功能的依賴
Spring Boot通過(guò)提供眾多起步依賴降低項(xiàng)目依賴的復(fù)雜度。起步依賴本質(zhì)上是一個(gè)Maven項(xiàng)目對(duì)象模型(Project Object Model荠锭, POM)旱眯,定義了對(duì)其他庫(kù)的傳遞依賴,這些東西加在一起即支持某項(xiàng)功能节沦。很多起步依賴的命名都暗示了它們提供的某種或某類功能键思。
例如,我們想要?jiǎng)?chuàng)建一個(gè)web項(xiàng)目甫贯,用Thymeleaf來(lái)定義Web視圖吼鳞,用Spring Data JPA來(lái)把閱讀列表持久化到數(shù)據(jù)庫(kù)里,姑且先用嵌入式的H2數(shù)據(jù)庫(kù)叫搁,最后還要添加測(cè)試依賴test赔桌。在Maven項(xiàng)目的pom.xml文件里看起來(lái)是這樣的:
<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的構(gòu)建文件里看起來(lái)是這樣的:
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 覆蓋起步依賴引入的傳遞依賴
你也可以通過(guò)構(gòu)建工具中的功能,選擇性地覆蓋它們引入的傳遞依賴的版本號(hào)渴逻,排除傳遞依賴疾党,當(dāng)然還可以為那些Spring Boot起步依賴沒(méi)有涵蓋的庫(kù)指定依賴。
以Spring Boot的Web起步依賴為例惨奕,它傳遞依賴了Jackson JSON庫(kù)雪位。如果你正在構(gòu)建一個(gè)生產(chǎn)或消費(fèi)JSON資源表述的REST服務(wù),那它會(huì)很有用梨撞。但是雹洗,要構(gòu)建傳統(tǒng)的面向人類用戶的Web應(yīng)用程序,你可能用不上Jackson卧波。雖然把它加進(jìn)來(lái)也不會(huì)有什么壞處时肿,但排除掉它的傳遞依賴,可以為你的項(xiàng)目瘦身港粱。
在Maven里螃成,可以用<exclusions>元素來(lái)排除傳遞依賴。下面這個(gè)引入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>
另一方面,也許項(xiàng)目需要Jackson寸宏,但你需要用另一個(gè)版本的Jackson來(lái)進(jìn)行構(gòu)建宁炫,而不是Web起步依賴?yán)锏哪莻€(gè)。假設(shè)Web起步依賴引用了Jackson 2.3.4击吱,但你需要使用2.4.3①淋淀。在Maven里,你可以直接在pom.xml中表達(dá)訴求覆醇,就像這樣:
<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 自動(dòng)配置
Spring Boot會(huì)為常見(jiàn)配置場(chǎng)景進(jìn)行自動(dòng)配置永脓。如果Spring Boot在應(yīng)用程序的Classpath里發(fā) 現(xiàn) H2 數(shù) 據(jù) 庫(kù) 的 庫(kù) 袍辞, 那 么 它 就 自 動(dòng) 配 置 一 個(gè) 嵌 入 式 H2 數(shù) 據(jù) 庫(kù) 。 如 果 在 Classpath 里 發(fā) 現(xiàn)JdbcTemplate常摧,那么它還會(huì)為你配置一個(gè)JdbcTemplate的Bean搅吁。你無(wú)需操心那些Bean的配置,Spring Boot會(huì)做好準(zhǔn)備落午,隨時(shí)都能將其注入到你的Bean里谎懦。
簡(jiǎn)而言之, Spring Boot的自動(dòng)配置是一個(gè)運(yùn)行時(shí)(更準(zhǔn)確地說(shuō)溃斋,是應(yīng)用程序啟動(dòng)時(shí))的過(guò)程界拦,考慮了眾多因素,才決定Spring配置應(yīng)該用哪個(gè)梗劫,不該用哪個(gè)享甸。舉幾個(gè)例子,下面這些情況都是Spring Boot的自動(dòng)配置要考慮的梳侨。
- Spring的JdbcTemplate是不是在Classpath里蛉威?如果是,并且有DataSource的Bean走哺,則自動(dòng)配置一個(gè)JdbcTemplate的Bean蚯嫌。
- Thymeleaf是不是在Classpath里?如果是丙躏,則配置Thymeleaf的模板解析器齐帚、視圖解析器以及模板引擎。
- Spring Security是不是在Classpath里彼哼?如果是,則進(jìn)行一個(gè)非诚娼瘢基本的Web安全設(shè)置敢朱。
每當(dāng)應(yīng)用程序啟動(dòng)的時(shí)候, Spring Boot的自動(dòng)配置都要做將近200個(gè)這樣的決定,涵蓋安全拴签、集成孝常、持久化、 Web開(kāi)發(fā)等諸多方面蚓哩。所有這些自動(dòng)配置就是為了盡量不讓你自己寫配置构灸。
3.2.1 原理分析
在向應(yīng)用程序加入Spring Boot時(shí),有個(gè)名為spring-boot-autoconfigure的JAR文件岸梨,其中包含了很多配置類喜颁。每個(gè)配置類都在應(yīng)用程序的Classpath里,都有機(jī)會(huì)為應(yīng)用程序的配置添磚加瓦曹阔。這些配置類里有用于Thymeleaf的配置半开,有用于Spring Data JPA的配置,有用于Spiring MVC的配置赃份,還有很多其他東西的配置寂拆,你可以自己選擇是否在Spring應(yīng)用程序里使用它們。
所有這些配置如此與眾不同抓韩,原因在于它們利用了Spring的條件化配置纠永,這是Spring 4.0引入的新特性。條件化配置允許配置存在于應(yīng)用程序中谒拴,但在滿足某些特定條件之前都忽略這個(gè)配置尝江。
Spring Boot運(yùn)用條件化配置的方法是,定義多個(gè)特殊的條件化注解彪薛,并將它們用到配置類上茂装。表2-1列出了Spring Boot提供的條件化注解。
我們可以看一下DataSourceAutoConfiguration里的這個(gè)片段(這是Spring Boot自動(dòng)配置庫(kù)的一部分):
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
...
}
如你所見(jiàn)善延, DataSourceAutoConfiguration添加了@Configuration注解少态,它從其他配置類里導(dǎo)入了一些額外配置,還自己定義了一些Bean易遣。最重要的是彼妻, DataSourceAutoConfiguration上添加了@ConditionalOnClass注解,要求Classpath里必須要有DataSource和EmbeddedDatabaseType豆茫。如果它們不存在侨歉,條件就不成立,DataSourceAutoConfiguration提供的配置都會(huì)被忽略掉揩魂。
3.2.2 通過(guò)屬性文件外置配置
Spring Boot自動(dòng)配置的Bean提供了300多個(gè)用于微調(diào)的屬性幽邓。當(dāng)你調(diào)整設(shè)置時(shí),只要在環(huán)境變量火脉、 Java系統(tǒng)屬性牵舵、 JNDI(Java Naming and Directory Interface)柒啤、命令行參數(shù)或者屬性文件里進(jìn)行指定就好了。
實(shí)際上畸颅, Spring Boot應(yīng)用程序有多種設(shè)置途徑担巩。 Spring Boot能從多種屬性源獲得屬性,包括如下幾處:
(1) 命令行參數(shù)
(2) java:comp/env里的JNDI屬性
(3) JVM系統(tǒng)屬性
(4) 操作系統(tǒng)環(huán)境變量
(5) 隨機(jī)生成的帶random.*前綴的屬性(在設(shè)置其他屬性時(shí)没炒,可以引用它們涛癌,比如${random.long})
(6) 應(yīng)用程序以外的application.properties或者appliaction.yml文件
(7) 打包在應(yīng)用程序內(nèi)的application.properties或者appliaction.yml文件
(8) 通過(guò)@PropertySource標(biāo)注的屬性源
(9) 默認(rèn)屬性
這個(gè)列表按照優(yōu)先級(jí)排序,也就是說(shuō)送火,任何在高優(yōu)先級(jí)屬性源里設(shè)置的屬性都會(huì)覆蓋低優(yōu)先級(jí)的相同屬性拳话。例如,命令行參數(shù)會(huì)覆蓋其他屬性源里的屬性漾脂。
application.properties和application.yml文件能放在以下四個(gè)位置假颇。
(1) 外置,在相對(duì)于應(yīng)用程序運(yùn)行目錄的/config子目錄里骨稿。
(2) 外置笨鸡,在應(yīng)用程序運(yùn)行的目錄里。
(3) 內(nèi)置坦冠,在config包內(nèi)形耗。
(4) 內(nèi)置辙浑,在Classpath根目錄激涤。
同樣,這個(gè)列表按照優(yōu)先級(jí)排序判呕。也就是說(shuō)倦踢, /config子目錄里的application.properties會(huì)覆蓋應(yīng)用程序Classpath里的application.properties中的相同屬性。此外侠草,如果你在同一優(yōu)先級(jí)位置同時(shí)有application.properties和application.yml辱挥,那么application.yml里的屬性會(huì)覆蓋application.properties里的屬性。
3.2.3 使用 Profile 進(jìn)行配置
當(dāng)應(yīng)用程序需要部署到不同的運(yùn)行環(huán)境時(shí)边涕,一些配置細(xì)節(jié)通常會(huì)有所不同晤碘。比如,數(shù)據(jù)庫(kù)連接的細(xì)節(jié)在開(kāi)發(fā)環(huán)境下和測(cè)試環(huán)境下就會(huì)不一樣功蜓,在生產(chǎn)環(huán)境下又不一樣园爷。 Spring Framework從Spring 3.1開(kāi)始支持基于Profile的配置。 Profile是一種條件化配置式撼,基于運(yùn)行時(shí)激活的Profile童社,會(huì)使用或者忽略不同的Bean或配置類。
例如著隆,我們就能為SecurityConfig加上@Profile注解:
@Profile("production")
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
但由于Spring Boot的自動(dòng)配置替你做了太多的事情叠洗,要找到一個(gè)能放置@Profile的地方還真不怎么方便甘改。幸運(yùn)的是, Spring Boot支持為application.properties和application.yml里的屬性配置Profile灭抑。
① 使用特定于Profile的屬性文件
如果你正在使用application.properties,可以創(chuàng)建額外的屬性文件抵代,遵循application-{profile}.properties這種命名格式腾节,這樣就能提供特定于Profile的屬性了。
在日志這個(gè)例子里荤牍,開(kāi)發(fā)環(huán)境的配置可以放在名為application-development.properties的文件里案腺,配置包含日志級(jí)別和輸出到控制臺(tái):
logging.level.root=DEBUG
對(duì)于生產(chǎn)環(huán)境, application-production.properties會(huì)將日志級(jí)別設(shè)置為WARN或更高級(jí)別康吵,并將日志寫入日志文件:
logging.path=/var/logs/
logging.file=BookWorm.log
logging.level.root=WARN
與此同時(shí)劈榨,那些并不特定于哪個(gè)Profile或者保持默認(rèn)值(以防萬(wàn)一有哪個(gè)特定于Profile的配置不指定這個(gè)值)的屬性,可以繼續(xù)放在application.properties里.
② 使用多Profile YAML文件進(jìn)行配置
如果使用YAML來(lái)配置屬性晦嵌,則可以遵循與配置文件相同的命名規(guī)范同辣,即創(chuàng)建application-{profile}.yml這樣的YAML文件,并將與Profile無(wú)關(guān)的屬性繼續(xù)放在application.yml里惭载。但既然用了YAML旱函,你就可以把所有Profile的配置屬性都放在一個(gè)application.yml文件里。舉例來(lái)說(shuō)描滔,我們可以像下面這樣聲明日志配置:
logging:
level:
root: INFO
---
spring:
profiles: development
logging:
level:
root: DEBUG
---
spring:
profiles: production
logging:
path: /tmp/
file: BookWorm.log
level:
root: WARN
4棒妨、小結(jié)
這篇博文中主要介紹了Spring Boot四個(gè)核心內(nèi)容中的起步依賴和自動(dòng)配置的內(nèi)容,因?yàn)轼B(niǎo)哥認(rèn)為這兩個(gè)部分的內(nèi)容相對(duì)比較重要含长。盡管使用Spring Boot開(kāi)發(fā)給我們帶來(lái)了諸多便利券腔,但是其本質(zhì)上還是Spring,想要用好Spring Boot拘泞,我們?nèi)匀恍枰ド钊氲貙W(xué)習(xí)Spring纷纫。