【SDK开发】SpringBoot启动时读取配置文件初始化类

背景介绍:SDK开发时,常常需要读取一些默认的配置文件,这些配置文件是由调用SDK工具包的
应用方配置。因而我们需要对这些配置文件进行读取后对SDK中的一些Bean进行初始化,体现了Bean加载顺序的重要性。

1、实体类

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

import java.util.LinkedHashMap;
import java.util.Map;

@ConfigurationProperties(prefix = "zxl.test")
public class PropertiesList {
    private Map<String, Properties> properties = new LinkedHashMap<>();

    private String adc;

    private String time1 = "60"; // 如果没有就赋默认值

    private String time2 = "900"; // 如果没有就赋默认值

    ......

    public Map<String, HccStorageProperties> getProperties() {
        return properties;
    }

    public void setProperties(Map<String, HccStorageProperties> properties) {
        this.properties = properties;
    }
	......
}
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Properties {
    private String a;

    private String b;

    private String c;
	......
}
import lombok.Getter;
import lombok.Setter;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;

@Configuration // 相当于xml中的Beans标签
@ConfigurationProperties(prefix = "url") // 读取配置文件中url开头的参数
@PropertySource(value = "classpath:Url.properties", ignoreResourceNotFound = true) // 读取Url.properties文件,如果没有则忽略,不报错
@Primary // 当有多个相同类型的bean时,使用@Primary来赋予bean更高的优先级
@Getter
@Setter
public class UrlProperties {
    private String url1 = "地址......";
}

2、配置类

import java.util.Map;
import java.util.Objects;

import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import zxl.test.it.hcc.storage.TestManager;
import zxl.test.properties.Properties;
import zxl.test.properties.PropertiesList;
import zxl.test.TestImpl;

// 1、ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类。
// 2、使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean。
// 3、实现该接口的类拥有注册bean的能力。
public class ImportConfig implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private PropertiesList propertiesList;

    /**
     * 三、服务启动时,第三个调用方法,给TestImpl类实例化注入到Bean容器中,此时propertiesList已经有值
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        Map<String, Properties> properties = propertiesList.getProperties();
        properties.forEach((key, value) -> {
            GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition(); // 注册Bean的类
            genericBeanDefinition.setBeanClass(TestImpl.class); // 设置自定义Bean的类
            ConstructorArgumentValues cav = new ConstructorArgumentValues();
            cav.addGenericArgumentValue(value);
            genericBeanDefinition.setConstructorArgumentValues(cav); // 设置自定义Bean的值
            String beanName = TestManager.getName(key); // 设置自定义Bean在Bean容器中的唯一名称
            registry.registerBeanDefinition(beanName, genericBeanDefinition); // 注册自定义Bean到Bean容器中
        });
    }

    /**
     * 一、服务启动时,最先调用这个方法,给propertiesList绑定值
     */
    @Override
    public void setEnvironment(Environment environment) {
        propertiesList = Binder.get(environment) // 获取到环境的Bingder
            // 读取配置文件中对应前缀的值,并绑定到PropertiesList中
            .bind(getPropertiesPrefix(PropertiesList.class), PropertiesList.class)
            .orElse(new PropertiesList()); // 绑定失败则取默认值,重新new实例
    }

    // 获得PropertiesList配置类中ConfigurationProperties注解中读取的参数前缀
    private String getPropertiesPrefix(Class<?> tClass) {
        return Objects.requireNonNull(AnnotationUtils.getAnnotation(tClass, ConfigurationProperties.class)).prefix();
    }
}
import java.util.Objects;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotatedTypeMetadata;

import zxl.test.properties.PropertiesList;

// 实现了Condition接口必定重现matches方法,目的是为了进行Bean注入时的条件判断
public class TestCondition implements Condition {
    /**
     * 二、随后调用这个方法,确定@Conditional(value = TestCondition.class)是否注入Bean
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return Binder.get(context.getEnvironment()) // 获取到环境的Bingder
            .bind(getPropertiesPrefix(PropertiesList.class), PropertiesList.class) // 将PropertiesList绑定到环境中
            .isBound(); // 绑定成功则为true,失败为false
    }

    // 获得PropertiesList配置类中ConfigurationProperties注解中读取的参数前缀
    private String getPropertiesPrefix(Class<?> tClass) {
        return Objects.requireNonNull(AnnotationUtils.getAnnotation(tClass, ConfigurationProperties.class)).prefix();
    }
}
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;

import zxl.test.TestManager;
import zxl.test.properties.PropertiesList;
import zxl.test.properties.UrlProperties;
import zxl.test.TestService;

@Configuration // 相当于xml中的Beans标签
// 让使用了 @ConfigurationProperties 注解的类生效,并且将该类注入到 IOC 容器中,交由 IOC 容器进行管理
@EnableConfigurationProperties({PropertiesList.class, UrlProperties.class})
@Import(ImportConfig.class) // 此处作用为导入ImportBeanDefinitionRegistrar的实现类
public class ManagerConfiguration {
    /**
     * 四、将TestService注入Bean容器
     */
    @Bean
    @Conditional(value = TestCondition.class) // 只有实现了Condition接口并重写的matches方法返回为true才注入Bean容器
    public TestService testService() {
        return new TestService();
    }

    /**
     * 五、HccStorageManager注入Bean容器
     */
    @Bean
    @Primary
    @Conditional(value = TestCondition.class) // 只有实现了Condition接口并重写的matches方法返回为true才注入Bean容器
    @ConditionalOnBean(value = TestService.class) // 只有Bean容器中已经存在TestService的Bean实例,才会注入Bean容器
    public TestManager testManager(PropertiesList propertiesList) {
        return new TestManager(propertiesList);
    }
}

3、整体Bean初始化顺序

(1)ImportConfig类实现了EnvironmentAware接口

​ 首先调用setEnvironment()方法,利用Binder类将配置文件中的zxl.test开头的数据绑定到PropertiesList类中。

(2)TestCondition类实现了Condition接口

​ 再调用matches()方法,利用Binder类判断是否将配置文件中的zxl.test开头的数据绑定到PropertiesList类中,绑定成功则返回true,失败则返回false。

(3)ImportConfig类实现了ImportBeanDefinitionRegistrar接口

​ 再调用registerBeanDefinitions()方法实现自定义Bean注入到IOC容器中。

(4)ManagerConfiguration类

​ 有**@Configuration注解(自定义Bean注册完后,开始执行该注解相关的Bean注册),@EnableConfigurationProperties({PropertiesList.class, UrlProperties.class})注解(让使用了@ConfigurationProperties** 注解的类生效,并且将该类注入到 IOC 容器中,交由 IOC 容器进行管理),**@Import(ImportConfig.class)**注解(导入ImportBeanDefinitionRegistrar的实现类)。

​ 标有**@Bean注解的进行Bean注册到IOC容器,根据@Conditional(value = TestCondition.class)注解和@ConditionalOnBean(value = TestService.class)注解判断该Bean是否注入,@Primary注解则表示当有多个相同类型的Bean**时,使用@Primary来赋予bean更高的优先级。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值