EnableConfigurationProperties、ConfigurationProperties、ConditionalOnProperty和 ConditionalOnBean注解

1、基本介绍

        SpringBoot提供了让我们告别传统通过Xml配置文件往Spring容器注入对象的新方式。而这几个注解,能够帮助我们更好地实现将yml文件中的属性映射到一个JAVA对象中。

1.0  基础数据准备

需求:搭建一个第三方客户端,第三方的基础信息从配置文件中读取。

yml文件中的自定义属性

# 第三方配置信息
wechat:
  enable: true
  url: http://www.baidu.com
  username: UMR
  password: 123456

SpringBoot启动类

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

2、@EnableConfigurationProperties注解的使用

         Enable开头的注解,都是表示"启用"的意思。@EnableConfigurationProperties注解的作用是启用对@ConfigurationProperties注解的支持,确保@ConfigurationProperties修饰的类可以被自动注册到Spring容器中。

        @EnableConfigurationProperties,都是和@ConfigurationProperties一起使用,它单独使用不会有任何效果。但是,@ConfigurationProperties注解可以单独使用。

/**
 * 第三方配置类,用来读取配置文件中的相关信息
 */
@EnableConfigurationProperties(WechatConfig.class)
@ConfigurationProperties(prefix = "wechat")
public class WechatConfig {
    private Boolean enable;
    private String url;
    private String username;
    private String password;

    public WechatConfig() {
        System.out.println("配置文件加载");
    }

    public Boolean getEnable() {
        return enable;
    }

    public void setEnable(Boolean enable) {
        this.enable = enable;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    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;
    }
}
/**
 * 第三方客户端。
 */
@Component
public class WechatTerminalClient {
    private String baseUrl;
    private String username;
    private String password;

    //通过构造方法的形式引入配置类
    public WechatTerminalClient(WechatConfig config) {
        this.baseUrl = config.getUrl();
        this.username = config.getUsername();
        this.password = config.getPassword();
        System.out.println(String.format("Wechat客户端加载完毕,读取到的url是%s,用户名是%s,密码是%s", baseUrl, username, password));
    }
}

       然后,启动项目会报错提示:

       这个错误的意思是Spring容器中找不到这个bean,也就是说EnableConfigurationProperties注解后面跟的WechatConfig类没有被注册到容器中去。

        这是因为@EnableConfigurationProperties需要放在启动类,或者被Spring容器管理的bean中,不然SpringBoot启动的时候,是扫描不到这个注解的。(@Component、@Configuration等等这些注解修饰的类,就会生成Spring容器管理的bean,换句话说,就是要放到这些注解修饰下的类才行)

而一般都是把这个注解直接放在启动类下面。

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

这样就正常启动了 

3、@ConfigurationProperties注解的使用

        首先明确一点,虽然@ConfigurationProperties注解和@Configuration很像,但是它不具备将修饰的类自动创建一个Bean对象,并交给Spring容器管理的功能

        @ConfigurationProperties的作用是快速绑定配置文件(yml、properties文件)中的属性值到一个JAVA对象当中。注解中有一个prefix属性,prefix的属性值必须和配置文件中的前缀相同。这样,类的成员变量才会在SpringBoot启动时,通过set方法获取配置文件中这个前缀下的同名属性。(乱写不会报错,但获取的数据会是空的)

3.0  搭配方式

@ConfigurationProperties一共有2种搭配使用方式。

第一种方式,就是和@EnableConfigurationProperties一起使用,上面写了,这里就不重复了。

第二种方式,就是和@Bean、@Component或者@Configuration注解一起使用。

3.1  和@Configuration注解一起使用

启动类还原,去掉@EnableConfigurationProperties注解

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

然后,配置类添加@Configuration注解就可以了

/**
 * 配置类
 */
@Configuration
@ConfigurationProperties(value = "wechat")
public class WechatConfig {
    private Boolean enable;
    private String url;
    private String username;
    private String password;

    public WechatConfig() {
        System.out.println("配置文件加载");
    }

    public Boolean getEnable() {
        return enable;
    }

    public void setEnable(Boolean enable) {
        this.enable = enable;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    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;
    }
}

3.2  和@Bean注解一起使用

改动比较大,但也是我在开发过程中经常使用的方式。

        首先,第三方客户端需要调整,客户端不再通过@Component创建Bean对象,而是提供一个有参构造方法,通过@Bean注解创建第三方客户端的Bean对象。

/**
 * 第三方客户端
 */
public class WechatTerminalClient {
    private String baseUrl;
    private String username;
    private String password;

    
    public WechatTerminalClient(String url,
                                String username,
                                String password) {
        this.baseUrl = url;
        this.username = username;
        this.password = password;
        System.out.println(String.format("Wechat客户端加载完毕,读取到的url是%s,用户名是%s,密码是%s", baseUrl, username, password));
    }
}

        然后,调整配置类,通过@Bean注解,来注册第三方客户端的Bean并放到Spring容器中。

/**
 * 配置类
 */
@Configuration
public class WechatConfig {

    /**
     * 创建第三方客户端的Bean,并交给容器管理
     *
     * @param wechat 封装了配置文件信息的对象
     * @return
     */
    @Bean
    public WechatTerminalClient wechatClient(Wechat wechat) {
        return new WechatTerminalClient(wechat.getUrl(), wechat.getUsername(), wechat.getPassword());
    }

    /**
     * 静态内部类,负责读取配置文件中的属性值
     */
    @Configuration
    @ConfigurationProperties(prefix = "wechat")
    public static class Wechat {
        private Boolean enable;
        private String url;
        private String username;
        private String password;

        public Boolean getEnable() {
            return enable;
        }

        public void setEnable(Boolean enable) {
            this.enable = enable;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        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;
        }
    }

}

4、@ConditionalOnProperty和 @ConditionalOnClass 条件注解

        以Conditional开头的注解,这类都是条件注解。比如,你想实现一个功能,只有在满足特定条件的时候,才会生成Bean放到容器中,这一类以 "Conditional" 开头的注解,就可以实现。

4.1  @ConditionalOnProperty注解

在配置类WechatConfig上,加入@ConditionalOnProperty注解

@ConditionalOnProperty注解的属性

prefix:前缀。

name:匹配哪个属性名。

havingValue:匹配哪个属性值。

matchIfMissing:如果没匹配到,是否加载该类。默认值false,表示没匹配到属性值时,不加载该类。

/**
 * 配置类
 */
@Configuration
@ConditionalOnProperty(prefix = "wechat", name = "enable", havingValue = "true")
public class WechatConfig {

    public WechatConfig() {
        System.out.println("WechatConfig的无参构造方法执行了");
    }

    /**
     * 创建第三方客户端的Bean,并交给容器管理
     *
     * @param wechat 封装了配置文件信息的对象
     * @return
     */
    @Bean
    public WechatTerminalClient wechatClient(Wechat wechat) {
        System.out.println("加载WechatTerminalClient的Bean");
        return new WechatTerminalClient(wechat.getUrl(), wechat.getUsername(), wechat.getPassword());
    }

    /**
     * 静态内部类,负责读取配置文件中的属性值
     */
    @Configuration
    @ConfigurationProperties(prefix = "wechat")
    public static class Wechat {
        private Boolean enable;
        private String url;
        private String username;
        private String password;

        public Boolean getEnable() {
            return enable;
        }

        public void setEnable(Boolean enable) {
            this.enable = enable;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        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;
        }
    }

}

@ConditionalOnProperty(prefix = "wechat", name = "enable", havingValue = "true")

如果配置文件中,wechat前缀下面的enable属性,它的值是true,则加载WechatConfig配置类,否则,不创建WechatConfig的bean对象。

匹配成功,启动时正常创建

修改yml文件中enable的值,将值由true改成false。这个时候,就不匹配havingValue指定的属性值

wechat:
  enable: false
  url: http://www.baidu.com
  username: UMR
  password: 123456

可以看见,虽然项目正常启动,但是控制台就不会输出这段日志,说明没有自动创建WechatConfig类的对象。

4.2  @ConditionalOnBean注解

将配置文件的enable由false重新改为true

# 第三方配置信息
wechat:
  enable: true
  url: http://www.baidu.com
  username: UMR
  password: 123456

@ConditionalOnBean:只有当容器中已经存在指定类型的 bean 时,才注册当前的 bean

创建第三方客户端的bean对象时,加入@ConditionalOnBean条件注解,保证项目中存在Wechat类型的bean时,才创建第三方客户端的bean。


/**
 * 配置类
 */
@Configuration
@ConditionalOnProperty(prefix = "wechat", name = "enable", havingValue = "true")
public class WechatConfig {
    public WechatConfig() {
        System.out.println("WechatConfig的无参构造方法执行了");
    }

    /**
     * 创建第三方客户端的Bean,并交给容器管理
     *
     * @param wechat 封装了配置文件信息的对象
     * @return
     */
    @Bean
    @ConditionalOnBean(Wechat.class)
    public WechatTerminalClient wechatClient(Wechat wechat) {
        System.out.println("加载WechatTerminalClient的Bean");
        return new WechatTerminalClient(wechat.getUrl(), wechat.getUsername(), wechat.getPassword());
    }
    
    @Bean
    @ConditionalOnMissingBean(value = {WechatTerminalClient.class})
    public WechatTerminalClient wechatClient2(){
        System.out.println("如果容器中不存在WechatTerminalClient类型的bean,则创建该bean");
        return new WechatTerminalClient("123","456","789");
    }

    /**
     * 静态内部类,负责读取配置文件中的属性值
     */
    @Configuration
    @ConfigurationProperties(prefix = "wechat")
    public static class Wechat {
        private Boolean enable;
        private String url;
        private String username;
        private String password;

        public Boolean getEnable() {
            return enable;
        }

        public void setEnable(Boolean enable) {
            this.enable = enable;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        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;
        }
    }

}

@ConditionalOnMissingBean则相反,如果在容器中找不到指定类型的bean,才会注册当前的bean,如果找得到,就不会注册进容器里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值