利用 Spring Boot 中的 @ConfigurationProperties,優雅綁定配置參數

使用 @Value("${property}") 註釋注入配置屬性有時會很麻煩,尤其是當你使用多個屬性或你的數據是分層的時候。

Spring Boot 引入了一個可替換的方案 —— @ConfigurationProperties 來注入屬性。

JavaBean 屬性綁定

@Data
@ConfigurationProperties("my.service")
public class MyProperties {
    
    // 我們可以簡單地用一個值初始化一個字段來定義一個默認值
    private boolean enabled = true;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    @Data
    public static class Security {

        private String username;

        private String password;
        // 如果這個屬性配置的話,默認是“USER”
        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

    }
}

在配置文件中進行如下配置:

my:
  service:
    enabled: true
    remoteAddress: 127.0.0.1
    security:
     username: csx
     password: passwoed
     roles:
       - role1
       - role2

最後生成的 Bean 的屬性如下:

{
  "enabled": true,
  "remoteAddress": "127.0.0.1",
  "security": {
    "username": "csx",
    "password": "passwoed",
    "roles": [
      "role1",
      "role2"
    ]
  }
}

以上的綁定當時需要提供默認的構造函數,以及get/setter方法。

並且不支持 JavaBean 中的靜態成員變量的數據綁定

另外,@ConfigurationProperties 還有兩個其他屬性。

@ConfigurationProperties( value = "my.service",
                          ignoreInvalidFields = false,
                          ignoreUnknownFields = false)

ignoreInvalidFields:是否忽略非法值,比如將一個字符串 “foo” 賦值給 bool 值,不忽略的話會報啓動異常。

ignoreUnknownFields:對於多餘的配置是否會報異常。

構造函數綁定

有些情況下,我們需要綁定的 JavaBean 是不可變的(防止配置注入 Bean 以後,開發者在程序中錯誤地將配置改掉了)。這種情況下我們可以使用構造函數形式的綁定,只提供 getter 方法。

@Getter
@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {

    private boolean enabled;
    private InetAddress remoteAddress;
    private final Security security;

    public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }

    @Getter
    public static class Security {

        private String username;
        private String password;
        private List<String> roles;

        public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }
    }
}

@DefaultValue 可以指定默認值。

使用構造函數綁定的方式,只能 @EnableConfigurationProperties 或者 @ConfigurationPropertiesScan 的方式激活 Bean。而不能使用 @Component、@Bean 或者 @Import 的方式進行數據綁定。

如果你的類有多個構造函數,可以直接指定使用哪個。

@ConstructorBinding
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
    this.enabled = enabled;
    this.remoteAddress = remoteAddress;
    this.security = security;
}

激活方式

方式一:添加 @Component 註解

上面的方式需要保證 MyProperties 能被 Spring 掃到。

@Data
@Component
@ConfigurationProperties("my.service")
public class MyProperties {
    
}

方式二:通過 @Bean 方法

@Configuration
public class ServiceConfig {

    @Bean
    public MyProperties myProperties(){
        return new MyProperties();
    }
}

方式三:@EnableConfigurationProperties(推薦)

@Configuration
@EnableConfigurationProperties(MyProperties.class)
public class ServiceConfig {

}

方式四:@ConfigurationPropertiesScan

@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {

}

怎麼使用

我們通過配置在 Spring 容器中生成了配置 Bean,那麼需要怎麼使用他們呢?

@Service
public class MyService {
    // 依賴注入
    @Autowired
    private MyProperties properties;

    public void service(){
        System.out.println(properties.getRemoteAddress());
    }

}

@Service
public class MyService {

    private MyProperties properties;
    // 通過構造函數注入,一般推薦這種方式
    public MyService(MyProperties properties) {
        this.properties = properties;
    }

    public void service(){
        System.out.println(properties.getRemoteAddress());
    }

}

給第三方類綁定值

假如某些類不是你自己開發的,你也想使用 @ConfigurationProperties 的方式給他綁定值,那麼可以進行下面的方式進行配置。

@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "another")
    public AnotherComponent anotherComponent() {
        return new AnotherComponent();
    }

}

寬鬆綁定原則(Relaxed Binding)

所謂的寬鬆綁定原則是指:並不是 JavaBean 中的屬性必須要和配置文件中的一致才能綁定數據,context-path 也能綁定到 contextPath 屬性上。下面舉個列子:

@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

下面的幾種方式,都能將配置文件或者環境變量中的值綁定到 firstName 上。

形式 使用場景
my.main-project.person.first-name 推薦使用在 .properties and .yml files.
my.main-project.person.firstName Standard camel case syntax.
my.main-project.person.first_name 推薦使用在 .properties and .yml files.
MY_MAINPROJECT_PERSON_FIRSTNAME 推薦使用在系統環境變量讀取配置時使用

和 @Value 對比

@Value 是 Spring Framework 中的註解,而 @ConfigurationProperties 是在 Spring Boot 中引入的。

img

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章