從本質(zhì)上來(lái)說(shuō)尚揣,Spring Boot就是Spring掖举,它做了那些沒(méi)有它你自己也會(huì)去做的SpringBean配置。謝天謝地方篮,幸好有Spring,你不用再寫(xiě)這些樣板配置了匕得,可以專注于應(yīng)用程序的邏輯巾表,這些才是應(yīng)用程序獨(dú)一無(wú)二的東西。
spring的介紹
啟動(dòng)引導(dǎo)spring
package readinglist;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReadingListApplication {
public static void main(String[] args) {
SpringApplication.run(ReadingListApplication.class, args);
}
}
@SpringBootApplication:開(kāi)啟組件掃描和自動(dòng)配置
SpringApplication.run 負(fù)責(zé)啟動(dòng)引導(dǎo)應(yīng)用程序
@SpringBootApplication 將三個(gè)有用的注解組合在了一起考阱。
- Spring的 @Configuration :標(biāo)明該類(lèi)使用Spring基于Java的配置惠猿。雖然本書(shū)不會(huì)寫(xiě)太多
配置,但我們會(huì)更傾向于使用基于Java而不是XML的配置姜凄。 - Spring的 @ComponentScan :?jiǎn)⒂媒M件掃描趾访,這樣你寫(xiě)的Web控制器類(lèi)和其他組件才能被
自動(dòng)發(fā)現(xiàn)并注冊(cè)為Spring應(yīng)用程序上下文里的Bean。本章稍后會(huì)寫(xiě)一個(gè)簡(jiǎn)單的Spring MVC
控制器申鱼,使用 @Controller 進(jìn)行注解云头,這樣組件掃描才能找到它。 - Spring Boot 的 @EnableAutoConfiguration : 這 個(gè) 不 起 眼 的 小 注 解 也 可 以 稱 為
@Abracadabra
① 匣砖,就是這一行配置開(kāi)啟了Spring Boot自動(dòng)配置的魔力昏滴,讓你不用再寫(xiě)成
篇的配置了。
spring的自動(dòng)配置
在maven的依賴?yán)锩嫖覀儠?huì)看到一些starter之類(lèi)的依賴拂共,這個(gè)是啟動(dòng)依賴姻几,比如hibernate势告,在依賴之后spring就會(huì)為我們做一個(gè)自動(dòng)配置的事情培慌,那么我們?nèi)绻胍远x的配置的話柑爸,就需要用自定義的配置來(lái)覆蓋這個(gè)自定義的。
這里有個(gè)不錯(cuò)的例子:當(dāng)你在應(yīng)用程序里添加安全特性時(shí)馅而,自動(dòng)配置做得還不夠好譬圣。安全配置并不是放之四海而皆準(zhǔn)的,圍繞應(yīng)用程序安全有很多決策要做屯蹦,Spring Boot不能替你做決定绳姨。
雖然Spring Boot為安全提供了一些基本的自動(dòng)配置,但是你還是需要自己覆蓋一些配置以滿足特定的安全要求脑蠕。想知道如何用顯式的配置來(lái)覆蓋自動(dòng)配置跪削,我們先從為閱讀列表應(yīng)用程序添加SpringSecurity入手。在了解自動(dòng)配置提供了什么之后晃跺,我們?cè)賮?lái)覆蓋基礎(chǔ)的安全配置毫玖,以滿足特定的
場(chǎng)景需求。
首先我們配置一個(gè)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ReaderRepository readerRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").access("hasRole('READER')")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true");
}
3.1 覆蓋 Spring Boot自動(dòng)配置
@Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return readerRepository.findOne(username);
}
});
}
}
那么自動(dòng)配置那邊是怎么檢測(cè)到這個(gè)類(lèi)然后并且覆蓋自動(dòng)配置類(lèi)呢?
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ EnableWebSecurity.class })
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
@ConditionalOnWebApplication
public class SpringBootWebSecurityConfiguration {
...
}
如你所見(jiàn)励背, SpringBootWebSecurityConfiguration 上加了好幾個(gè)注解砸西≈啡澹看到 @ConditionalOnClass 注解后衅疙,你就應(yīng)該知道Classpath里必須要有 @EnableWebSecurity 注解。
@ConditionalOnWebApplication 說(shuō) 明 這 必 須 是 個(gè) Web 應(yīng) 用 程 序 喧伞。 @ConditionalOn-MissingBean 注解才是我們的安全配置類(lèi)代替 SpringBootWebSecurityConfiguration 的關(guān)鍵所在绩郎。
@ConditionalOnMissingBean 注解要求當(dāng)下沒(méi)有WebSecurityConfiguration 類(lèi)型的Bean肋杖。雖然表面上我們并沒(méi)有這么一個(gè)Bean,但通過(guò)在 SecurityConfig 上添加 @EnableWeb-Security 注解状植,我們實(shí)際上間接創(chuàng)建了一個(gè) WebSecurityConfiguration Bean津畸。所以在自動(dòng)
配置時(shí),這個(gè)Bean就已經(jīng)存在了吩案, @ConditionalOnMissingBean 條件不成立帝簇, SpringBoot-WebSecurityConfiguration 提供的配置就被跳過(guò)了。
雖然Spring Boot的自動(dòng)配置和 @ConditionalOnMissingBean 讓你能顯式地覆蓋那些可以自動(dòng)配置的Bean丧肴,但并不是每次都要做到這種程度芋浮。讓我們來(lái)看看怎么通過(guò)設(shè)置幾個(gè)簡(jiǎn)單的配置屬性調(diào)整自動(dòng)配置組件吧。
通過(guò)屬性文件外置配置
在處理應(yīng)用安全時(shí)镇草,你當(dāng)然會(huì)希望完全掌控所有配置瘤旨。不過(guò),為了微調(diào)一些細(xì)節(jié)因宇,比如改改端口號(hào)和日志級(jí)別,便放棄自動(dòng)配置察滑,這是一件讓人羞愧的事贺辰。為了設(shè)置數(shù)據(jù)庫(kù)URL,是配置一個(gè)屬性簡(jiǎn)單魂爪,還是完整地聲明一個(gè)數(shù)據(jù)源的Bean簡(jiǎn)單滓侍?答案不言自明,不是嗎捺球?事實(shí)上夕冲,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)行指定就好了
- 比如springboot程序開(kāi)啟時(shí)候會(huì)有一個(gè)banner就是一個(gè)圖形化的代碼出現(xiàn)秧廉,如果你想要禁用的話,就在application.properties文件里面加上配置:spring.main.show-banner=false嚼锄,當(dāng)然如果你喜歡的話蔽豺,也可以創(chuàng)建名為application.yml的YAML文件:
spring:
main:
show-banner: false
- 比如你想要修改tomcat的端口號(hào):
server:
port: 8000
- 要設(shè)置日志級(jí)別茫虽,你可以創(chuàng)建以 logging.level 開(kāi)頭的屬性,后面是要日志名稱濒析。如果根日志級(jí)別要設(shè)置為 WARN 号杏,但Spring Security的日志要用 DEBUG 級(jí)別,可以在application.yml里加入
logging:
path: /var/logs/
file: BookWorm.log
level:
root: WARN
org:
springframework:
security: DEBUG
另外主经,你也可以把Spring Security的包名寫(xiě)成一行:
logging:
level:
root: WARN
org.springframework.security: DEBUG
- 配置數(shù)據(jù)源
spring:
datasource:
url: jdbc:mysql://localhost/readinglist
username: dbuser
password: dbpass
driver-class-name: com.mysql.jdbc.Driver
以下內(nèi)容:
實(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根目錄亿柑。
應(yīng)用程序 Bean 的配置外置
@RequestMapping("/")
@ConfigurationProperties(prefix="amazon")
public class ReadingListController {
private String associateId;
private ReadingListRepository readingListRepository;
@Autowired
public ReadingListController(
ReadingListRepository readingListRepository) {
this.readingListRepository = readingListRepository;
}
public void setAssociateId(String associateId) {
this.associateId = associateId;
}
如你所見(jiàn), ReadingListController 現(xiàn)在有了一個(gè) associateId 屬性秘症,還有對(duì)應(yīng)的 setAssociateId() 方法式矫,用它可以設(shè)置該屬性采转。 readersBooks() 現(xiàn)在能通過(guò) amazonID 這個(gè)鍵把a(bǔ)ssociateId 的值放入模型。
棒極了板熊!現(xiàn)在就剩一個(gè)問(wèn)題了——從哪里能取到 associateId 的值察绷。
請(qǐng)注意, ReadingListController 上加了 @ConfigurationProperties 注解容劳,這說(shuō)明該Bean的屬性應(yīng)該是(通過(guò)setter方法)從配置屬性值注入的闸度。說(shuō)得更具體一點(diǎn), prefix 屬性說(shuō)明ReadingListController 應(yīng)該注入帶 amazon 前綴的屬性留量。
綜合起來(lái),我們指定 ReadingListController 的屬性應(yīng)該從帶 amazon 前綴的配置屬性中進(jìn)行注入忆绰。 ReadingListController 只有一個(gè)setter方法可岂,就是設(shè)置 associateId 屬性用的setter方法。因此,設(shè)置Amazon Associate ID唯一要做的就是添加 amazon.associateId 屬性致开,把它加入支持的任一屬性源位置里即可萎馅。
在application.yml里設(shè)置:
amazon:
associateId: habuma-20
在一個(gè)類(lèi)里收集屬性
雖然在 ReadingListController 上加上 @ConfigurationProperties 注解跑起來(lái)沒(méi)問(wèn)題,但這并不是一個(gè)理想的方案飒货。 ReadingListController 和Amazon沒(méi)什么關(guān)系峭竣,但屬性的前綴卻是 amazon 皆撩,這看起來(lái)難道不奇怪嗎?再說(shuō)扛吞,后續(xù)的各種功能可能需要在 ReadingListController 里新增配置屬性滥比,而它們和Amazon無(wú)關(guān)。
與其在 ReadingListController 里加載配置屬性濒持,還不如創(chuàng)建一個(gè)單獨(dú)的Bean寺滚,為它加上@ConfigurationProperties 注解,讓這個(gè)Bean收集所有配置屬性玛迄。代碼清單3-5里的 Amazon-Properties 就是一個(gè)例子,它用于加載Amazon相關(guān)的配置屬性虏杰。
@Component
@ConfigurationProperties("amazon")
public class AmazonProperties {
private String associateId;
public void setAssociateId(String associateId) {
this.associateId = associateId;
}
public String getAssociateId() {
return associateId;
}
}
有了加載 amazon.associateId 配置屬性的 AmazonProperties 后,我們可以調(diào)整
ReadingListController (如代碼清單3-6所示)瘸彤,讓它從注入的 AmazonProperties 中獲取
Amazon Associate ID笛钝。
使用 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或配置類(lèi)庆械。
@Profile("production")
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
這里用的 @Profile 注解要求運(yùn)行時(shí)激活 production Profile菌赖,這樣才能應(yīng)用該配置琉用。如果production Profile沒(méi)有激活,就會(huì)忽略該配置奴紧,而此時(shí)缺少其他用于覆蓋的安全配置晶丘,于是應(yīng)用自動(dòng)配置的安全配置唐含。
向application.yml里添加 spring.profiles.active 屬性:
spring:
profiles:
active: production
使用多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