在Spring Boot中使用 @ConfigurationProperties 注解, @EnableConfigurationProperties


Spring Boot 提供了另一种方式 ,能够根据类型校验和管理application中的bean。 这里会介绍如何使用@ConfigurationProperties
继续使用mail做例子。配置放在mail.properties文件中。属性必须命名规范才能绑定成功。举例:
1 protocol and PROTOCOL will be bind to protocol field of a bean
2 smtp-auth , smtp_auth , smtpAuth will be bind to smtpAuth field of a bean
3 smtp.auth will be bind to … hmm to smtp.auth field of a bean!

Spring Boot 使用一些松的规则来绑定属性到@ConfigurationProperties bean 并且支持分层结构(hierarchical structure)。
开始创建一个@ConfigurationProperties bean:

package com.dxz.property;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(locations = "classpath:mail.properties", ignoreUnknownFields = false, prefix = "mail")
public class MailProperties {
    private String host;
    private int port;
    private String from;
    private String username;
    private String password;
    private Smtp smtp;

    // ... getters and setters
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Smtp getSmtp() {
        return smtp;
    }

    public void setSmtp(Smtp smtp) {
        this.smtp = smtp;
    }
    
    @Override
    public String toString() {
        return "MailProperties [host=" + host + ", port=" + port + ", from=" + from + ", username=" + username
                + ", password=" + password + ", smtp=" + smtp + "]";
    }

    public static class Smtp {
        private boolean auth;
        private boolean starttlsEnable;

        public boolean isAuth() {
            return auth;
        }

        public void setAuth(boolean auth) {
            this.auth = auth;
        }

        public boolean isStarttlsEnable() {
            return starttlsEnable;
        }

        public void setStarttlsEnable(boolean starttlsEnable) {
            this.starttlsEnable = starttlsEnable;
        }

    }
}

如下属性中创建 ( mail.properties ):

mail.host=localhost
mail.port=25
mail.smtp.auth=false
mail.smtp.starttls-enable=false
mail.from=me@localhost
mail.username=duan
mail.password=duan123456

上例中我们用@ConfigurationProperties注解就可以绑定属性了。ignoreUnknownFields = false告诉Spring Boot在有属性不能匹配到声明的域的时候抛出异常。开发的时候很方便! prefix 用来选择哪个属性的prefix名字来绑定。
请注意setters 和 getters 需要在@ConfigurationProperties bean中创建! 与@Value注解相反。

我们需要用属性来配置 application。 有至少两种方式来创建@ConfigurationProperties。即可以搭配@Configuration 注解来提供 @Beans 也可以单独使用并注入 @Configuration bean。

方案1:定义spring的一个实体bean装载配置文件信息,其它要使用配置信息是注入该实体bean

package com.dxz.property3;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(locations = "classpath:mail.properties", ignoreUnknownFields = false, prefix = "mail")
public class MailProperties {
    private String host;
    private int port;
    private String from;
    private String username;
    private String password;
    private Smtp smtp;

    // ... getters and setters
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Smtp getSmtp() {
        return smtp;
    }

    public void setSmtp(Smtp smtp) {
        this.smtp = smtp;
    }
    
    @Override
    public String toString() {
        return "MailProperties [host=" + host + ", port=" + port + ", from=" + from + ", username=" + username
                + ", password=" + password + ", smtp=" + smtp + "]";
    }

    public static class Smtp {
        private boolean auth;
        private boolean starttlsEnable;

        public boolean isAuth() {
            return auth;
        }

        public void setAuth(boolean auth) {
            this.auth = auth;
        }

        public boolean isStarttlsEnable() {
            return starttlsEnable;
        }

        public void setStarttlsEnable(boolean starttlsEnable) {
            this.starttlsEnable = starttlsEnable;
        }

    }
}

启动及测试类:

package com.dxz.property3;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
//@EnableConfigurationProperties(MailProperties.class)
public class TestProperty3 {

    @Autowired
    private MailProperties mailProperties;
    
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    @ResponseBody
    public String hello() {
        System.out.println("mailProperties" + mailProperties);
        return "hello world";
    }

    public static void main(String[] args) {
        //SpringApplication.run(TestProperty1.class, args);
        new SpringApplicationBuilder(TestProperty3.class).web(true).run(args);

    }
}

结果:

mailPropertiesMailProperties [host=localhost, port=25, from=me@localhost, username=duan, password=duan123456, smtp=com.dxz.property3.MailProperties$Smtp@37cebacb]

方案2:@Bean+@ConfigurationProperties

我们还可以把@ConfigurationProperties还可以直接定义在@bean的注解上,这是bean实体类就不用@Component和@ConfigurationProperties了
package com.dxz.property4;


public class MailProperties {
    private String host;
    private int port;
    private String from;
    private String username;
    private String password;
    private Smtp smtp;

    // ... getters and setters
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Smtp getSmtp() {
        return smtp;
    }

    public void setSmtp(Smtp smtp) {
        this.smtp = smtp;
    }
    
    @Override
    public String toString() {
        return "MailProperties [host=" + host + ", port=" + port + ", from=" + from + ", username=" + username
                + ", password=" + password + ", smtp=" + smtp + "]";
    }

    public static class Smtp {
        private boolean auth;
        private boolean starttlsEnable;

        public boolean isAuth() {
            return auth;
        }

        public void setAuth(boolean auth) {
            this.auth = auth;
        }

        public boolean isStarttlsEnable() {
            return starttlsEnable;
        }

        public void setStarttlsEnable(boolean starttlsEnable) {
            this.starttlsEnable = starttlsEnable;
        }

    }
}

配置类(启动类)

package com.dxz.property4;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class TestProperty4 {

    @Bean
    @ConfigurationProperties(locations = "classpath:mail.properties", prefix = "mail")
    public MailProperties mailProperties(){
        MailProperties mp = new MailProperties();
        System.out.println("zheli " + mp);
        return mp;

    }

    public static void main(String[] args) {
        //SpringApplication.run(TestProperty1.class, args);
        new SpringApplicationBuilder(TestProperty4.class).web(true).run(args);

    }
}

测试类:

package com.dxz.property4;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/task")
public class TaskController {

@Autowired 
MailProperties mailProperties;

@RequestMapping(value = {"/",""})
public String hellTask(){
    System.out.println("mailProperties" +mailProperties);  
    return "hello task !!";
}

}

结果:

方案3:@ConfigurationProperties + @EnableConfigurationProperties

我们和上面例子一样注解属性,然后用 Spring的@Autowire来注入 mail configuration bean:

package com.dxz.property;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(locations = "classpath:mail.properties", ignoreUnknownFields = false, prefix = "mail")
public class MailProperties {
    private String host;
    private int port;
    private String from;
    private String username;
    private String password;
    private Smtp smtp;

    // ... getters and setters
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Smtp getSmtp() {
        return smtp;
    }

    public void setSmtp(Smtp smtp) {
        this.smtp = smtp;
    }
    
    @Override
    public String toString() {
        return "MailProperties [host=" + host + ", port=" + port + ", from=" + from + ", username=" + username
                + ", password=" + password + ", smtp=" + smtp + "]";
    }

    public static class Smtp {
        private boolean auth;
        private boolean starttlsEnable;

        public boolean isAuth() {
            return auth;
        }

        public void setAuth(boolean auth) {
            this.auth = auth;
        }

        public boolean isStarttlsEnable() {
            return starttlsEnable;
        }

        public void setStarttlsEnable(boolean starttlsEnable) {
            this.starttlsEnable = starttlsEnable;
        }

    }
}

启动类及测试类:

package com.dxz.property;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
@EnableConfigurationProperties(MailProperties.class)
public class TestProperty1 {

    @Autowired
    private MailProperties mailProperties;
    
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    @ResponseBody
    public String hello() {
        System.out.println("mailProperties" + mailProperties);
        return "hello world";
    }

    public static void main(String[] args) {
        //SpringApplication.run(TestProperty1.class, args);
        new SpringApplicationBuilder(TestProperty1.class).web(true).run(args);

    }
}

结果:

请注意@EnableConfigurationProperties注解该注解是用来开启对@ConfigurationProperties注解配置Bean的支持。也就是@EnableConfigurationProperties注解告诉Spring Boot 能支持@ConfigurationProperties。如果不指定会看到如下异常:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.dxz.property.MailProperties] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

注意: 还有其他办法 (Spring Boot 总是有其他办法!) 让@ConfigurationProperties beans 被添加 – 用@Configuration或者 @Component注解, 这样就可以在 component scan时候被发现了。

总结:

@ConfigurationProperties很方便使用。 比用@Value注解好吗? 在特定的方案中是的,这只是一个选择问题。
看下Spring Boot的文档有更多的关于 typesafe configuration 属性

属性文件自动提示

一般在我们开发中,通过IDE打开属性文件会产生一个自动提示。这种自定义提示也可以使用到我们自定义配置类中。

首先我们需要在项目中加入processor jar包

dependencies {
    compileOnly "org.springframework.boot:spring-boot-configuration-processor"
}

第二步我们需要配置META-INF/spring-configuration-metadata.json文件来描述。但是代码量挺大的,为了方便我们可以通过IDE来生成,这里使用的是idea。

在idea设置中搜索Annotation Processors,接下来勾住Enable annonation processing就完成了。

我们可以在编译后的文件中看到自动生成的spring-configuration-metadata.json。

附上配图: