SpringBoot 的關鍵詞是「約定俗成」,它根據長久以來的 Spring 開發配置經驗,整理出一套適用、廣泛、你們都承認的配置方案。因此 SpringBoot 的學習過程當中心態必定要放寬,不必太去鑽牛角尖,重點是配置和自定義...java
Spring Boot 簡化了基於Spring的應用開發,爲Spring平臺及第三方庫提供了開箱即用的設置,你只須要「run」就能建立一個獨立的,產品級的 Spring 應用。git
SpringBoot 的 Startsers 是以一個依賴描述符的集合,包含了不少搭建,快速運行項目所須要的依賴,並提供一致的,可管理傳遞性的依賴集。你能夠獲取全部 Spring 及相關技術的一站式服務,而不須要翻閱示例代碼,拷貝粘貼大量的依賴描述符。全部官方的 starters 遵循類似的命名模式:spring-boot-starter-* 。github
SpringBoot 的 Auto-configuration 設計成能夠跟 Starters 一塊兒很好的使用,AutoConfiguration 會根據你所依賴的 jar 包,會盡最大努力去自動配置你的應用。web
Spring Boot 每次發佈都關聯一個 Spring 框架的基礎版本,因此強烈建議你不要本身指定Spring版本。spring
spring-boot-devtools 是 SpringBoot 中內置的一個開發者工具 ,用於自重啓,功能固然沒有 Jrebel 那麼強大,但正常的開發也差很少夠用了。spring-boot-devtools 默認檢測 classpath 路徑下的文件,只要目錄下的文件有變更,它就會自動重啓。chrome
自動重啓跟 LiveReload 能夠一塊兒很好的工做。瀏覽器
若是你使用 JRebel,自動重啓將禁用以支持動態類加載。緩存
<!-- 一、在運行一個完整的,打包過的應用時(Java -jar),會禁用開發者工具。 二、防止 devtools 傳遞到項目中的其餘模塊,須要設置依賴級別 optional 三、只要 classpath 下的文件有變更(Eclipse 中文件保存就能重啓,IDEA 中須要 Build ——> Build Project),系統就會自動重啓。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
spring: devtools: restart: # devtools 排除不須要檢測的資源 和 增長額外須要監測的資源 exclude: application.yml additional-paths: src/main/webapp # 是否重啓,若是設置爲false禁用,依舊會初始化重啓類加載器,但它不會監控文件變化 enabled: true # 觸發器文件,只有在修改該文件的時候,才能觸發工程重啓 #trigger-file: trigger-file livereload: # 內嵌的 LiveReload 服務器,能夠在資源改變時,觸發瀏覽器更新,禁用設置爲false enabled: true
LiveReload 是一個 spring-boot-devtools 模塊中的內嵌服務器,它能夠在資源改變時觸發瀏覽器更新,LiveReload 瀏覽器擴展的谷歌插件下載地址:https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei ,若是不想使用該功能的話,能夠在 application.yml 中禁用,如上。這工具仍是很值得推薦的,能夠大大的節約開發時間。服務器
SpringApplication 姑且稱它爲 SpringBoot 的引導類吧!它將爲咱們建立正確類型的 Spring 上下文 ApplicationContext 。默認狀況下,SpringApplication 根據咱們開發的是否爲web應用(能夠手動指定是否爲 Web 應用)決定使用 AnnotationConfigApplicationContext 或 AnnotationConfigEmbeddedWebApplicationContext。除此以外,SpringApplication 還有一些無關痛癢的設置,好比:是否打印 Banner 這些的。app
經常須要在 SpringApplication 加載前或者退出後作一些相關的操做,好比初始化一些信息,關閉一些流、文件 什麼的。怎麼實現呢?
實現 CommandLineRunner 或者 ApplicationRunner 接口能夠在 SpringApplication 啓動後,run() 方法運行前執行一些特殊的代碼。
實現 ExitCodeGenerator 接口能夠在 Application 退出後返回特殊的特徵碼,用於 SpringApplication.exit() 時使用。
實現 DisposableBean 接口,用於在 SpringApplication 退出後(SpringApplication.exit())實現本身的一些邏輯,或者使用 @PreDestroy 註解。
@Component public class ApplicationArgumentsBean implements CommandLineRunner, ApplicationRunner, ExitCodeGenerator, DisposableBean { private static final Logger logger = LoggerFactory.getLogger(ApplicationArgumentsBean.class); /** * 若是運行的時候使用 java -jar *.jar --debug logfile.txt * 則:debug = true,files=["logfile.txt"] * * @param args */ @Autowired public ApplicationArgumentsBean(ApplicationArguments args) { boolean debug = args.containsOption("debug"); List<String> files = args.getNonOptionArgs(); } @Override public void run(ApplicationArguments args) throws Exception { logger.info("重寫 ApplicationRunner 的 run 方法:{}", args.containsOption("debug")); } @Override public void run(String... args) throws Exception { logger.info("重寫 CommandLineRunner 的 run 方法:{}", args); } @Override public int getExitCode() { return 0; } @Override public void destroy() throws Exception { logger.info("重寫 DisposableBean 的 destroy 方法,用於在 SpringApplication 退出後執行一些操做"); } @PreDestroy public void predestroy() { logger.info("使用 @PreDestroy 用於在 SpringApplication 退出後執行一些操做"); } }
@EnableAutoConfiguration @Configuration @ComponentScan public class Example { public static void main(String[] args) { // 咱們須要將Example.class做爲參數傳遞給run方法,以此告訴SpringApplication誰是主要的Spring組件 SpringApplication app = new SpringApplication(Example.class); // 手動調用setWebApplicationType() 指定爲 web 應用 app.setWebApplicationType(WebApplicationType.SERVLET); // 設置打印 Banner 的方式 app.setBannerMode(Banner.Mode.LOG); // 是否將啓動時的命令行屬性添加到 Environment app.setAddCommandLineProperties(true); ConfigurableApplicationContext run = app.run(args); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { SpringApplication.exit(run, (ExitCodeGenerator) run.getBean("applicationArgumentsBean")); } }); } }
tips:建議將應用的main類放在其餘類所在包的頂層(root package)。
SpringBoot 的默認配置文件是 application.yml 或者 application.properties,若是想要引入其餘的 properties 文件 或者 yml 文件,該怎麼操做呢?
properties 文件引入較爲簡單,跟 Spring 同樣。在配置類上使用 @PropertySource 註解引入,在其餘地方使用 @Value 註解讀取。
咱們先從 SpringBoot 的默認配置文件 application.yml 文件聊起,application.yml 的文件內容,是能夠經過 @Value 的方式讀取到的,好比 @Value("${server.port}") 這樣。究其緣由的話,應該是 SpringBoot 底層把 ApplicationContext 註冊進 PropertySourcesPlaceholderConfigurer 致使的吧!
那麼咱們自定義的 yml 文件要怎麼引入呢?看了網上的一些教程,不少人推薦用以下這種方式:
@Bean public PropertySourcesPlaceholderConfigurer properties() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); yaml.setResources(new ClassPathResource("my.yml")); configurer.setProperties(yaml.getObject()); return configurer; }
這種方式,確實能夠用,經過這種方式把 yml 文件加載到 PropertySourcesPlaceholderConfigurer 後,經過 @Value 方式讀取到屬性值。可是!可是!原來的 application.yml 中的 @Value 屬性全獲取不到了,我猜測是否是 Bean 覆蓋致使的,我試着手動把 application.yml 和 my.yml 都加載進 PropertySourcesPlaceholderConfigurer ,以及使用 @Primary 註解,結果都無論用!因此就放棄了這種作法。
那要怎麼加載咱們自定義的 yml 文件呢 ?經過 YamlPropertiesFactoryBean 或者 YamlMapFactoryBean 類:
@Test public void test3() { YamlPropertiesFactoryBean yml = new YamlPropertiesFactoryBean(); yml.setResources(new ClassPathResource("my.yml")); Properties properties = yml.getObject(); Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Object, Object> entry = iterator.next(); logger.info("YamlPropertiesFactoryBean 讀取的配置文件內容是:{}-{}", entry.getKey(), entry.getValue()); } logger.info("--------華麗的分割線------------"); YamlMapFactoryBean yamlMapFactoryBean = new YamlMapFactoryBean(); yamlMapFactoryBean.setResources(new ClassPathResource("my.yml")); Map<String, Object> map = yamlMapFactoryBean.getObject(); Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Object> entry = it.next(); logger.info("YamlPropertiesFactoryBean 讀取的配置文件內容是:{}-{}", entry.getKey(), entry.getValue()); } }
另外須要說起的是 SpringBoot 還提供了 @ConfigurationProperties(prefix = "spring.datasource") 註解,將 yml 文件中的屬性直接轉換成 Bean 中的屬性(前提是有 set 方法),並且屬性的匹配很寬鬆,採用 Relaxed 綁定,以 firstName 舉例(可匹配firstName、first-name、first_name、FIRST_NAME)。以後再在啓動類中使用 @EnableConfigurationProperties(JavaConfig.class) 使之生效。
不一樣於 Spring 中的單元測試 —— 採用 @RunWith(SpringJUnit4ClassRunner.class) 和 @ContextConfiguration(locations = "classpath:applicationContext.xml"),SpringBoot 中使用 @RunWith(SpringRunner.class) 和 @SpringBootTest(classes = Example.class) 。
Spring Boot的@*Test註解會自動搜索主配置類,即便你沒有顯式定義它。(不過仍是建議顯示配置下)
你可使用@SpringBootTest的webEnvironment屬性定義怎麼運行測試:
MOKE -- 默認,加載WebApplicationContext,並提供一個mock servlet環境,使用該註解時內嵌servlet容器將不會啓動。若是classpath下不存在servlet APIs,該模式將建立一個常規的non-web ApplicationContext。
RANDOM_PORT -- 加載EmbeddedWebApplicationContext,並提供一個真實的servlet環境。使用該模式內嵌容器將啓動,並監聽在一個隨機端口。
DEFINED_PORT -- 加載EmbeddedWebApplicationContext,並提供一個真實的servlet環境。使用該模式內嵌容器將啓動,並監聽一個定義好的端口(好比application.properties中定義的或默認的8080端口)。
NONE -- 使用SpringApplication加載一個ApplicationContext,但不提供任何servlet環境(不論是mock仍是其餘)。
@RunWith(SpringRunner.class) @SpringBootTest(classes = Example.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class Test1 { @Autowired private TestRestTemplate testRestTemplate; @LocalServerPort // 用於注入測試用例實際使用的端口 private Integer localServerPort; static { //for localhost testing only javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() { public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { if (hostname.equals("localhost")) { return true; } return false; } }); } @Test public void Test1() { System.out.println("測試啓動的隨機端口是" + localServerPort); Area area = testRestTemplate.getForObject("/area/list", Area.class); System.out.println(area.getInfo()); } }
Spring Boot 提供一個 @MockBean 註解,可用於爲 ApplicationContext 中的bean定義一個Mockito mock,你可使用該註解添加新beans,或替換已存在的bean定義。該註解可直接用於測試類,也可用於測試類的字段,或用於@Configuration註解的類和字段。當用於字段時,建立mock的實例也會被注入。Mock beans每次調用完測試方法後會自動重置。
import static org.mockito.BDDMockito.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = Example.class) public class Test2 { @MockBean private UserSercice userSercice; @Test public void test2(){ User user = new User("111111", "張三", "15980292662", 23, 1); given(this.userSercice.getUserByUuid("111111")).willReturn(user); User userByUuid = userSercice.getUserByUuid("111111"); System.out.println(userByUuid); } }
tips:Spring測試框架在測試過程當中會緩存應用上下文,所以,只要你的測試共享相同的配置(不論是怎麼發現的),加載上下文的潛在時間消耗都只會發生一次。
一個完整的Spring Boot Starter 可能包含如下組件:
-- Autoconfigure模塊,包含自動配置類的代碼。好比一些能夠直接使用的 Bean 定義。
-- Starter模塊,提供 Autoconfigure模塊 及其餘有用的 jar 依賴,簡而言之,添加本 Starter 就能開始使用該library。
自動配置能夠捆綁在外部的jar包中,由Spring Boot提取 ———— Spring Boot會檢查你發佈的jar中是否存在META-INF/spring.factories文件,該文件中以EnableAutoConfiguration爲key的屬性列出你的配置類。
自動配置能夠關聯一個 "Starter",用於提供 auto-configuration 的代碼及須要引用的libraries。
Starter 實際上是一個空的模塊,它的惟一目的其實就是提供一個必要的依賴關係。
官方 Auto-configuration 示例:https://github.com/snicoll-demos/spring-boot-master-auto-configuration
演示源代碼地址:https://github.com/JMCuixy/Boot