一. 解決自動配置問題
Spring Boot自動配置總是嘗試盡最大努力去做正確的事,但有時候會失敗并且很難說出失敗原因棋嘲。
在每個Spring Boot ApplicationContext中都存在一個相當(dāng)有用的ConditionEvaluationReport。如果開啟 DEBUG 日志輸出灾杰,你將會看到它梅垄。如果你使用 spring-boot-actuator ,則會有一個autoconfig的端點溅漾,它將以JSON形式渲染該報告≈瘢可以使用它調(diào)試應(yīng)用程序添履,并能查看Spring Boot運行時都添加了哪些特性(及哪些沒添加)。
通過查看源碼和javadoc可以獲取更多問題的答案脑又。以下是一些經(jīng)驗:
1.查找名為 AutoConfiguration 的類并閱讀源碼暮胧,特別是 @Conditional 注解锐借,這可以幫你找出它們啟用哪些特性及何時啟用。 將 --debug 添加到命令行或添加系統(tǒng)屬性 -Ddebug 可以在控制臺查看日志往衷,該日志會記錄你的應(yīng)用中所有自動配置的決策钞翔。在一個運行的Actuator app中,通過查看autoconfig端點( /autoconfig 或等效的JMX)可以獲取相同信息席舍。
2.查找是 @ConfigurationProperties 的類(比如ServerProperties)并看下有哪些可用的外部配置選項布轿。 @ConfigurationProperties 類有一個用于充當(dāng)外部配置前綴的name屬性,因此 ServerProperties 的值為 prefix="server" 来颤,它的配置屬性有 server.port 汰扭, server.address 等。在運行的Actuator應(yīng)用中可以查看configprops端點福铅。
3.查看使用RelaxedEnvironment明確地將配置從Environment暴露出去萝毛。它經(jīng)常會使用一個前綴。
4.查看 @Value 注解滑黔,它直接綁定到Environment笆包。相比RelaxedEnvironment,這種方式稍微缺乏靈活性略荡,但它也允許松散的綁定庵佣,特別是OS環(huán)境變量(所以 CAPITALS_AND_UNDERSCORES 是 period.separated 的同義詞)。
5.查看 @ConditionalOnExpression 注解汛兜,它根據(jù)SpEL表達式的結(jié)果來開啟或關(guān)閉特性秧了,通常使用解析自Environment的占位符進行計算。
二.啟動前自定義Environment或ApplicationContext
每個SpringApplication都有ApplicationListeners和ApplicationContextInitializers序无,用于自定義上下文(context)或環(huán)境(environment)验毡。Spring Boot從 META-INF/spring.factories 下加載很多這樣的內(nèi)部使用的自定義。有很多方法可以注冊其他的自定義:
1.以編程方式為每個應(yīng)用注冊自定義帝嗡,通過在SpringApplication運行前調(diào)用它的 addListeners 和 addInitializers 方法來實現(xiàn)晶通。
2.以聲明方式為每個應(yīng)用注冊自定義,通過設(shè)置 context.initializer.classes 或 context.listener.classes 來實現(xiàn)哟玷。
3.以聲明方式為所有應(yīng)用注冊自定義狮辽,通過添加一個 META-INF/spring.factories 并打包成一個jar文件(該應(yīng)用將它作為一個庫)來實現(xiàn)。SpringApplication會給監(jiān)聽器(即使是在上下文被創(chuàng)建之前就存在的)發(fā)送一些特定的ApplicationEvents巢寡,然后也會注冊監(jiān)聽ApplicationContext發(fā)布的事件的監(jiān)聽器喉脖。查看Spring Boot特性章節(jié)中的Section 22.4, “Application events and listeners”可以獲取一個完整列表。
三.創(chuàng)建一個非web(non-web)應(yīng)用
- 不是所有的Spring應(yīng)用都必須是web應(yīng)用(或web服務(wù))抑月。如果你想在main方法中執(zhí)行一些代碼树叽,但需要啟動一個Spring應(yīng)用去設(shè)置需要的底層設(shè)施,那使用Spring Boot的 SpringApplication 特性可以很容易實現(xiàn)谦絮。 SpringApplication 會根據(jù)它是否需要一個web應(yīng)用來改變它的 ApplicationContext 類题诵。首先你需要做的是去掉servlet API依賴洁仗,如果不能這樣做(比如,基于相同的代碼運行兩個應(yīng)用)性锭,那你可以明確地調(diào)用 SpringApplication.setWebEnvironment(false) 或設(shè)置 applicationContextClass 屬性(通過Java API或使用外部配置)赠潦。你想運行的,作為業(yè)務(wù)邏輯的應(yīng)用代碼可以實現(xiàn)為一個 CommandLineRunner 草冈,并將上下文降級為一個 @Bean 定義她奥。
四.屬性&配置
1.外部化SpringApplication配置
-
SpringApplication已經(jīng)被屬性化(主要是setters),所以你可以在創(chuàng)建應(yīng)用時使用它的Java API修改它的行為怎棱×螅或者你可以使用properties文件中的 spring.main.* 來外部化(在應(yīng)用代碼外配置)這些配置。比如蹄殃,在 application.properties 中可能會有以下內(nèi)容:
spring.main.web_environment=false spring.main.show_banner=false
然后Spring Boot在啟動時將不會顯示banner携茂,并且該應(yīng)用也不是一個web應(yīng)用你踩。
2.改變應(yīng)用程序外部配置文件的位置
默認情況下诅岩,來自不同源的屬性以一個定義好的順序添加到Spring的 Environment 中(查看'Sprin Boot特性'章節(jié)的Chapter23, Externalized Configuration獲取精確的順序)。
為應(yīng)用程序源添加 @PropertySource 注解是一種很好的添加和修改源順序的方法带膜。傳遞給 SpringApplication 靜態(tài)便利設(shè)施(convenience)方法的類和使用 setSources() 添加的類都會被檢查吩谦,以查看它們是否有 @PropertySources ,如果有膝藕,這些屬性會被盡可能早的添加到 Environment 里式廷,以確保 ApplicationContext 生命周期的所有階段都能使用。以這種方式添加的屬性優(yōu)先于任何使用默認位置添加的屬性芭挽,但低于系統(tǒng)屬性滑废,環(huán)境變量或命令行參數(shù)。
你也可以提供系統(tǒng)屬性(或環(huán)境變量)來改變該行為:
1.spring.config.name ( SPRING_CONFIG_NAME )是根文件名袜爪,默認為 application 蠕趁。
2.spring.config.location ( SPRING_CONFIG_LOCATION )是要加載的文件(例如,一個classpath資源或一個URL)辛馆。SpringBoot為該文檔設(shè)置一個單獨的 Environment 屬性俺陋,它可以被系統(tǒng)屬性,環(huán)境變量或命令行參數(shù)覆蓋昙篙。不管你在environment設(shè)置什么腊状,Spring Boot都將加載上面討論過的 application.properties 。如果使用YAML苔可,那具有'.yml'擴展的文件默認也會被添加到該列表缴挖。
3.使用'short'命令行參數(shù)
-
有些人喜歡使用(例如) --port=9000 代替 --server.port=9000 來設(shè)置命令行配置屬性。你可以通過在application.properties中使用占位符來啟用該功能焚辅,比如:
server.port=${port:8080}
注:如果你繼承自 spring-boot-starter-parent POM醇疼,為了防止和Spring-style的占位符產(chǎn)生沖突硕并, maven-resources-plugins默認的過濾令牌(filter token)已經(jīng)從 ${*} 變?yōu)?@ (即 @maven.token@ 代替了 ${maven.token} )。如果已經(jīng)直接啟用maven對application.properties的過濾秧荆,你可能也想使用其他的分隔符替換默認的過濾令牌倔毙。
注:在這種特殊的情況下,端口綁定能夠在一個PaaS環(huán)境下工作乙濒,比如Heroku和Cloud Foundry陕赃,因為在這兩個平臺中 PORT 環(huán)境變量是自動設(shè)置的,并且Spring能夠綁定 Environment 屬性的大寫同義詞颁股。
4.使用YAML配置外部屬性
- YAML是JSON的一個超集么库,可以非常方便的將外部配置以層次結(jié)構(gòu)形式存儲起來。比如:
spring:
application:
name: cruncher
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost/test
server:
port: 9000
- 創(chuàng)建一個application.yml文件甘有,將它放到classpath的根目錄下诉儒,并添加snakeyaml依賴(Maven坐標為 org.yaml:snakeyaml ,如果你使用 spring-boot-starter 那就已經(jīng)被包含了)亏掀。一個YAML文件會被解析為一個Java Map<String,Object> (和一個JSON對象類似)忱反,Spring Boot會平伸該map,這樣它就只有1級深度滤愕,并且有period-separated的keys温算,跟人們在Java中經(jīng)常使用的Properties文件非常類似。 上面的YAML示例對應(yīng)于下面的application.properties文件:
spring.application.name=cruncher
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000
5.設(shè)置生效的Spring profiles
-
Spring Environment 有一個API可以設(shè)置生效的profiles间影,但通常你會設(shè)置一個系統(tǒng)profile(spring.profiles.active )或一個OS環(huán)境變量( SPRING_PROFILES_ACTIVE )注竿。比如,使用一個 -D 參數(shù)啟動應(yīng)用程序(記著把它放到main類或jar文件之前):
$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
-
在Spring Boot中魂贬,你也可以在application.properties里設(shè)置生效的profile巩割,例如:
spring.profiles.active=production
通過這種方式設(shè)置的值會被系統(tǒng)屬性或環(huán)境變量替換,但不會被SpringApplicationBuilder.profiles() 方法替換付燥。因此宣谈,后面的Java API可用來在不改變默認設(shè)置的情況下增加profiles。
6.根據(jù)環(huán)境改變配置
- 一個YAML文件實際上是一系列以 --- 線分割的文檔机蔗,每個文檔都被單獨解析為一個平坦的(flattened)map蒲祈。
- 如果一個YAML文檔包含一個 spring.profiles 關(guān)鍵字,那profiles的值(以逗號分割的profiles列表)將被傳入Spring的 Environment.acceptsProfiles() 方法萝嘁,并且如果這些profiles的任何一個被激活梆掸,對應(yīng)的文檔被包含到最終的合并中(否則不會)。
示例:
server:
port: 9000
---
spring:
profiles: development
server:
port: 9001
---
spring:
profiles: production
server:
port: 0
在這個示例中牙言,默認的端口是9000酸钦,但如果Spring profile 'development'生效則該端口是9001,如果'production'生效則它是0咱枉。
YAML文檔以它們遇到的順序合并(所以后面的值會覆蓋前面的值)卑硫。
想要使用profiles文件完成同樣的操作徒恋,你可以使用 application-${profile}.properties 指定特殊的,profile相關(guān)的值欢伏。
7.發(fā)現(xiàn)外部屬性的內(nèi)置選項
Spring Boot在運行時將來自application.properties(或.yml)的外部屬性綁定進一個應(yīng)用中入挣。在一個地方不可能存在詳盡的所有支持屬性的列表(技術(shù)上也是不可能的),因為你的classpath下的其他jar文件也能夠貢獻硝拧。
每個運行中且有Actuator特性的應(yīng)用都會有一個 configprops 端點径筏,它能夠展示所有邊界和可通過 @ConfigurationProperties 綁定的屬性。
附錄中包含一個application.properties示例障陶,它列舉了Spring Boot支持的大多數(shù)常用屬性滋恬。獲取權(quán)威列表可搜索 @ConfigurationProperties 和 @Value 的源碼,還有不經(jīng)常使用的 RelaxedEnvironment 抱究。
五.內(nèi)嵌的servlet容器
1.為應(yīng)用添加Servlet恢氯,F(xiàn)ilter或ServletContextListener
Servlet規(guī)范支持的Servlet,F(xiàn)ilter鼓寺,ServletContextListener和其他監(jiān)聽器可以作為 @Bean 定義添加到你的應(yīng)用中勋拟。需要格外小心的是,它們不會引起太多的其他beans的熱初始化侄刽,因為在應(yīng)用生命周期的早期它們已經(jīng)被安裝到容器里了(比如指黎,讓它們依賴你的DataSource或JPA配置就不是一個好主意)朋凉。你可以通過延遲初始化它們到第一次使用而不是初始化時來突破該限制州丹。
在Filters和Servlets的情況下,你也可以通過添加一個 FilterRegistrationBean 或 ServletRegistrationBean 代替或以及底層的組件來添加映射(mappings)和初始化參數(shù)杂彭。
2.改變HTTP端口
在一個單獨的應(yīng)用中墓毒,主HTTP端口默認為8080,但可以使用 server.port 設(shè)置(比如亲怠,在application.properties中或作為一個系統(tǒng)屬性)所计。由于 Environment 值的寬松綁定,你也可以使用 SERVER_PORT (比如团秽,作為一個OS環(huán)境變)主胧。
為了完全關(guān)閉HTTP端點,但仍創(chuàng)建一個WebApplicationContext习勤,你可以設(shè)置 server.port=-1 (測試時可能有用)踪栋。
3.使用隨機未分配的HTTP端口
- 想掃描一個未使用的端口(為了防止沖突使用OS本地端口)可以使用 server.port=0 。
4.發(fā)現(xiàn)運行時的HTTP端口
你可以通過日志輸出或它的EmbeddedServletContainer的EmbeddedWebApplicationContext獲取服務(wù)器正在運行的端口图毕。獲取和確認服務(wù)器已經(jīng)初始化的最好方式是添加一個 ApplicationListener<EmbeddedServletContainerInitializedEvent> 類型的 @Bean 夷都,然后當(dāng)事件發(fā)布時將容器pull出來。
使用 @WebIntegrationTests 的一個有用實踐是設(shè)置 server.port=0 予颤,然后使用 @Value 注入實際的('local')端口囤官。例如:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyspringbootApplication.class)
@WebIntegrationTest("server.port:0")
public class CityRepositoryIntegrationTests {
@Autowired
EmbeddedWebApplicationContext server;
@Value("${local.server.port}")
int port;
// ...
}
5.配置SSL
- SSL能夠以聲明方式進行配置冬阳,一般通過在application.properties或application.yml設(shè)置各種各樣的 server.ssl.* 屬性。例如:
server.port = 8443
server.ssl.key-store = classpath:keystore.jks
server.ssl.key-store-password = secret
server.ssl.key-password = another-secret
注:Tomcat要求key存儲(如果你正在使用一個可信存儲)能夠直接在文件系統(tǒng)上訪問党饮,即它不能從一個jar文件內(nèi)讀取肝陪。Jetty和Undertow沒有該限制。
- 使用類似于以上示例的配置意味著該應(yīng)用將不再支持端口為8080的普通HTTP連接刑顺。Spring Boot不支持通過application.properties同時配置HTTP連接器和HTTPS連接器见坑。如果你兩個都想要,那就需要以編程的方式配置它們中的一個捏检。推薦使用application.properties配置HTTPS荞驴,因為HTTP連接器是兩個中最容易以編程方式進行配置的。獲取示例可查看spring-boot-sample-tomcat-multi-connectors示例項目贯城。
6.配置Tomcat
通常你可以遵循Section 63.7, “Discover built-in options for external properties”關(guān)于 @ConfigurationProperties (這里主要的是 ServerProperties )的建議熊楼,但也看下 EmbeddedServletContainerCustomizer 和各種你可以添加的Tomcat-specific的 *Customizers 。
Tomcat APIs相當(dāng)豐富能犯,一旦獲取到 TomcatEmbeddedServletContainerFactory 鲫骗,你就能夠以多種方式修改它〔染В或核心選擇是添加你自己的 TomcatEmbeddedServletContainerFactory 执泰。
7.啟用Tomcat的多連接器(Multiple Connectors)
- 你可以將一個 org.apache.catalina.connector.Connector 添加到 TomcatEmbeddedServletContainerFactory ,這就能夠允許多連接器渡蜻,比如HTTP和HTTPS連接器:
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createSslConnector());
return tomcat;
}
private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
try {
File keystore = new ClassPathResource("keystore").getFile();
File truststore = new ClassPathResource("keystore").getFile();
connector.setScheme("https");
connector.setSecure(true);
connector.setPort(8443);
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keystore.getAbsolutePath());
protocol.setKeystorePass("changeit");
protocol.setTruststoreFile(truststore.getAbsolutePath());
protocol.setTruststorePass("changeit");
protocol.setKeyAlias("apitester");
return connector;
}
catch (IOException ex) {
throw new IllegalStateException("can't access keystore: [" + "keystore"
+ "] or truststore: [" + "keystore" + "]", ex);
}
}
8.在前端代理服務(wù)器后使用Tomcat
-
Spring Boot將自動配置Tomcat的 RemoteIpValve 术吝,如果你啟用它的話。這允許你透明地使用標準的 x-forwarded-for 和 xforwarded-proto 頭茸苇,很多前端代理服務(wù)器都會添加這些頭信息(headers)排苍。通過將這些屬性中的一個或全部設(shè)置為非空的內(nèi)容來開啟該功能(它們是大多數(shù)代理約定的值,如果你只設(shè)置其中的一個学密,則另一個也會被自動設(shè)置)淘衙。
server.tomcat.remote_ip_header=x-forwarded-for server.tomcat.protocol_header=x-forwarded-proto
-
如果你的代理使用不同的頭部(headers),你可以通過向application.properties添加一些條目來自定義該值的配置腻暮,比如:
server.tomcat.remote_ip_header=x-your-remote-ip-header server.tomcat.protocol_header=x-your-protocol-header
-
該值也可以配置為一個默認的彤守,能夠匹配信任的內(nèi)部代理的正則表達式。默認情況下哭靖,受信任的IP包括 10/8, 192.168/16,169.254/16 和 127/8具垫。可以通過向application.properties添加一個條目來自定義該值的配置款青,比如:
server.tomcat.internal_proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
注:只有在你使用一個properties文件作為配置的時候才需要雙反斜杠做修。如果你使用YAML,單個反斜杠就足夠了, 192.168.\d{1,3}.\d{1,3} 和上面的等價饰及。
- 另外蔗坯,通過在一個 TomcatEmbeddedServletContainerFactory bean中配置和添加 RemoteIpValve ,你就可以完全控制它的設(shè)置了燎含。
9.使用Tomcat7
- Tomcat7可用于Spring Boot宾濒,但默認使用的是Tomcat8。如果不能使用Tomcat8(例如屏箍,你使用的是Java1.6)绘梦,你需要改變classpath去引用Tomcat7。
9.1通過Maven使用Tomcat7
-
如果正在使用starter pom和parent赴魁,你只需要改變Tomcat的version屬性卸奉,比如,對于一個簡單的webapp或service:
<properties> <tomcat.version>7.0.59</tomcat.version> </properties> <dependencies> ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ... </dependencies>
9.2通過Gradle使用Tomcat7
-
你可以通過設(shè)置 tomcat.version 屬性改變Tomcat的版本:
ext['tomcat.version'] = '7.0.59' dependencies { compile 'org.springframework.boot:spring-boot-starter-web' }
10.使用@ServerEndpoint創(chuàng)建WebSocket端點
-
如果想在一個使用內(nèi)嵌容器的Spring Boot應(yīng)用中使用@ServerEndpoint颖御,你需要聲明一個單獨的ServerEndpointExporter @Bean:
@Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }
該bean將用底層的WebSocket容器注冊任何的被 @ServerEndpoint 注解的beans榄棵。當(dāng)部署到一個單獨的servlet容器時,該角色將被一個servlet容器初始化方法履行潘拱,ServerEndpointExporter bean也就不是必需的了疹鳄。
11.啟用HTTP響應(yīng)壓縮
- Spring Boot提供兩種啟用HTTP壓縮的機制;一種是Tomcat特有的,另一種是使用一個filter芦岂,可以配合Jetty瘪弓,Tomcat和Undertow。
11.1啟用Tomcat的HTTP響應(yīng)壓縮
-
Tomcat對HTTP響應(yīng)壓縮提供內(nèi)建支持禽最。默認是禁用的腺怯,但可以通過application.properties輕松的啟用:
server.tomcat.compression: on
-
當(dāng)設(shè)置為 on 時,Tomcat將壓縮響應(yīng)的長度至少為2048字節(jié)弛随。你可以配置一個整型值來設(shè)置該限制而不只是 on 瓢喉,比如:
server.tomcat.compression: 4096
-
默認情況下宁赤,Tomcat只壓縮某些MIME類型的響應(yīng)(text/html舀透,text/xml和text/plain)。你可以使用 server.tomcat.compressableMimeTypes 屬性進行自定義决左,比如:
server.tomcat.compressableMimeTypes=application/json,application/xml
11.2使用GzipFilter開啟HTTP響應(yīng)壓縮
如果你正在使用Jetty或Undertow愕够,或想要更精確的控制HTTP響應(yīng)壓縮,Spring Boot為Jetty的GzipFilter提供自動配置佛猛。雖然該過濾器是Jetty的一部分惑芭,但它也兼容Tomcat和Undertow。想要啟用該過濾器继找,只需簡單的為你的應(yīng)用添加 org.eclipse.jetty:jetty-servlets 依賴遂跟。
GzipFilter可以使用 spring.http.gzip.* 屬性進行配置。具體參考GzipFilterProperties。