1. 程式人生 > >Spring Boot自動配置(Auto-Configuration),@EnableAutoConfiguration,Spring Beans和依賴注入

Spring Boot自動配置(Auto-Configuration),@EnableAutoConfiguration,Spring Beans和依賴注入

自動配置(Auto-Configuration)

自動配置(auto-configuration)是Spring Boot最重要的特性之一,因為該特性會根據應用的classpath(這個主要是根據maven pom.xml決定),annotations和其他的一些java配置描述符自動配置Spring Boot應用。例如,如果在你的classpath中配置了H2(在pom.xml檔案中添加了H2的依賴),你不必維護任何資料連線,Spring Boot會自動配置一個記憶體資料庫。

Auto-configuration是非侵入性的,在任何時候你都可以開始定義你自己的配置來替換auto-configuration的特定部分。比如說,如果你添加了自己的DataSource bean,預設嵌入的資料庫支援將會失效。

如果你發現一些特定的自動配置類你不想應用,你可以使用 @EnableAutoConfiguration 註解的排除屬性來禁用它們。

import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class
}) public class MyConfiguration { }

如果類不在classpath中,你也可以使用註解的excludeName屬性並指定類的完全限定名(fully qualified name)來排除。你也可以通過spring.autoconfigure.exclude屬性來控制自動配置的類列表。

@EnableAutoConfiguration和@Enable<Technology>註解
在Spring FrameWork和它的一些模組如Spring Core, Spring Data,Spring AMQP, Spring Integration中,提供了@Enable<Technology>註解。如@EnableTransactionManagement, @EnableRabbit, @EnableIntegration就是上訴所提到的模組中的一部分。在Spring應用中,你可以根據“約定大於配置(convention over configuration)”的模式去使用它們,這樣就會讓你的應用非常容易開發且不用因為配置檔案太多而擔心維護的問題。

Spring Boot採取了這些註解的優點,在@EnableAutoConfiguration註解中的這些註解被用來執行自動配置(auto-configuration)。讓我們來近距離的看一下@EnableAutoConfiguration註解並看一看它背後的邏輯。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

我們知道,這個類會嘗試去猜測和配置應用中需要的bean。auto-configuration類是基於classpath(pom.xml的配置)的配置和應用中定義的beans被使用,但是發揮這種魔力的是org.springframework.boot.autoconfigure.EnableAutoConfigurationImportSelector類,該類可以找到所有必須的配置類。

EnableAutoConfigurationImportSelector類繼承自AutoConfigurationImportSelector類(不同版本的Spring Boot可能有所不同),該類有很多方法,但是要讓自動配置( auto-configuration)生效最重要的方法之一就是getCandidateConfiguration方法。

//...
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        Assert.notEmpty(configurations,
                "No auto configuration classes found in META-INF/spring.factories. If you "
                        + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}
//...

getCandidateConfigurations方法返回SpringFactoriesLoader.loadFactoryNames,SpringFactoriesLoader.loadFactories會查詢spring-boot-autoconfigure jar中定義的 META-INF/spring.factories檔案。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
...
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
....
....

可以看到,在spring.factories檔案中定義了在應用執行時所有可能自動配置的類。

構建程式碼

Spring Boot不需要使用任何特殊的程式碼結構, 然而, 這裡有一些有用的最佳實踐。

  1. 使用default包:當類沒有包含 package 宣告時, 它被認為處於 default package 下,通常不推薦並且要避免使用 default package。因為對於使用 @ComponentScan , @EntityScan 或 @SpringBootApplication 註解的Spring Boot應用來說, 來自每個jar的類都會被讀取, 這會帶來一些特殊的問題。

  2. 定位main應用類:建議將main應用類放在位於其他類上面的根包(root package) 中。通常使用 @EnableAutoConfiguration 註解你的main類, 並且暗地裡為某些項定義了一個基礎“search package”。 例如, 如果你正在編寫一個JPA應用,在被 @EnableAutoConfiguration所註解的類的包中的所有@Entity 註解的項都將會被檢索到。

使用root包允許在不需要指定basePackage屬性的情況下使用@ComponentScan註解。如果main類位於根包中, 你也可以使用 @SpringBootApplication 註解。

下面是一個典型的結構:
這裡寫圖片描述

Application.java 檔案將宣告 main 方法, 還有基本的@SpringBootApplication註解

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Spring Beans和依賴注入(dependency injection)

你可以自由地使用任何標準的Spring框架技術去定義beans和它們注入的依賴。 簡單起見, 我們經常使用 @ComponentScan 註解搜尋beans, 並結合 @Autowired 構造器注入。建議遵循Java推薦的包命名規範, 使用一個反轉的域名(例如 com.example.project ) 。

如果使用上面建議的結構組織程式碼(將應用類放到根包下) , 你可以新增@SpringBootApplication或@ComponentScan 註解而不需要任何引數。 你的所有應用程式元件( @Component , @Service , @Repository , @Controller 等) 將被自動註冊為Spring Beans。

下面是一個 @Service Bean的示例, 它使用構建器注入獲取一個需要的 RiskAssessor bean。

package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DatabaseAccountService implements AccountService {
    private final RiskAssessor riskAssessor;

    @Autowired
    public DatabaseAccountService(RiskAssessor riskAssessor) {
    this.riskAssessor = riskAssessor;
    } 
    // ...
}

如果bean只有一個建構函式,你可以忽略@Autowired。

@Service
public class DatabaseAccountService implements AccountService {
    private final RiskAssessor riskAssessor;
    public DatabaseAccountService(RiskAssessor riskAssessor) {
        this.riskAssessor = riskAssessor;
    }
    // ...
}

注意在上面的程式碼中如何使用構建器注入來允許 riskAssessor 欄位被標記為 final , 這意味著 riskAssessor 後續是不能改變的。

很多Spring Boot開發者總是使用 @Configuration , @EnableAutoConfiguration 和 @ComponentScan 註解他們的main類。 由於這些註解被如此頻繁地一塊使用 , Spring Boot提供一個方便的 @SpringBootApplication 選擇。@SpringBootApplication 註解等價於以預設屬性使用 @Configuration , @EnableAutoConfiguration 和 @ComponentScan 。