1. 程式人生 > >SpringBoot 之基礎學習篇.

SpringBoot 之基礎學習篇.

一、概念簡介

    SpringBoot 的關鍵詞是“約定俗成”,它根據長久以來的 Spring 開發配置經驗,整理出一套適用、普遍、大家都認可的配置方案。所以 SpringBoot 的學習過程中心態一定要放寬,沒必要太去鑽牛角尖,重點是配置和自定義...

    Spring Boot 簡化了基於Spring的應用開發,為Spring平臺及第三方庫提供了開箱即用的設定,你只需要“run”就能建立一個獨立的,產品級的 Spring 應用。

    SpringBoot 的 Startsers 是以一個依賴描述符的集合,包含了很多搭建,快速執行專案所需要的依賴,並提供一致的,可管理傳遞性的依賴集。你可以獲取所有 Spring 及相關技術的一站式服務,而不需要翻閱示例程式碼,拷貝貼上大量的依賴描述符。所有官方的 starters 遵循相似的命名模式:spring-boot-starter-* 。

    SpringBoot 的 Auto-configuration 設計成可以跟 Starters 一起很好的使用,AutoConfiguration 會根據你所依賴的 jar 包,會盡最大努力去自動配置你的應用。

    Spring Boot 每次釋出都關聯一個 Spring 框架的基礎版本,所以強烈建議你不要自己指定Spring版本。

二、spring-boot-devtools 開發者工具

    spring-boot-devtools 是 SpringBoot 中內建的一個開發者工具 ,用於自重啟,功能當然沒有 Jrebel 那麼強大,但正常的開發也差不多夠用了。spring-boot-devtools 預設檢測 classpath 路徑下的檔案,只要目錄下的檔案有變動,它就會自動重啟。

    自動重啟跟 LiveReload 可以一起很好的工作。

    如果你使用 JRebel,自動重啟將禁用以支援動態類載入。

    1、Maven 依賴

        <!--
             1、在執行一個完整的,打包過的應用時(Java -jar),會禁用開發者工具。
             2、防止 devtools 傳遞到專案中的其他模組,需要設定依賴級別 optional
             3、只要 classpath 下的檔案有變動(Eclipse 中檔案儲存就能重啟,IDEA 中需要 Build ——> Build Project),系統就會自動重啟。
        
--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>

    2、application.yml 配置

spring:
  devtools:
    restart:
      # devtools 排除不需要檢測的資源 和 增加額外需要監測的資源
      exclude: application.yml
      additional-paths: src/main/webapp
      # 是否重啟,如果設定為false禁用,依舊會初始化重啟類載入器,但它不會監控檔案變化
      enabled: true
      # 觸發器檔案,只有在修改該檔案的時候,才能觸發工程重啟
      #trigger-file: trigger-file
    livereload:
      # 內嵌的 LiveReload 伺服器,可以在資源改變時,觸發瀏覽器更新,禁用設定為false
      enabled: true

    3、spring-boot-devtools 和 LiveReload 搭配使用

    LiveReload 是一個 spring-boot-devtools 模組中的內嵌伺服器,它可以在資源改變時觸發瀏覽器更新,LiveReload 瀏覽器擴充套件的谷歌外掛下載地址:如果不想使用該功能的話,可以在 application.yml 中禁用,如上。這工具還是很值得推薦的,可以大大的節約開發時間。

三、SpringApplication

     SpringApplication 姑且稱它為 SpringBoot 的引導類吧!它將為我們建立正確型別的 Spring 上下文 ApplicationContext 。預設情況下,SpringApplication 根據我們開發的是否為web應用(可以手動指定是否為 Web 應用)決定使用 AnnotationConfigApplicationContext 或 AnnotationConfigEmbeddedWebApplicationContext。除此之外,SpringApplication 還有一些無關痛癢的設定,比如:是否列印 Banner 這些的。

    常常需要在 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 退出後執行一些操作");
    }
}
ApplicationArgumentsBean.java
@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 檔案,該怎麼操作呢?

    1、properties 檔案

    properties 檔案引入較為簡單,跟 Spring 一樣。在配置類上使用 @PropertySource 註解引入,在其他地方使用 @Value 註解讀取。

    2、yml 檔案

    我們先從 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) :

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Example.class)
public class Test1 {

    private static final Logger logger = LoggerFactory.getLogger(Test1.class);

    @Autowired
    private DataSource dataSource;

    @Test
    public void test1() {
        logger.info("@ConfigurationProperties 註解的實現效果:" + ((DruidDataSource) dataSource).getUrl());
    }

}