理解配置背后的 PropertySource 抽象

PropertySource

添加 PropertySource

  • < context:property-placeholder >
    把property文件中的属性,变成spring可以获取到的配置项,它的背后就是帮我们去注册一个propertyplaceholderconfigure 是spring3.1之后 提供的
  • PropertySourcesPlaceholderConfigurer
    • PropertyPlaceholderConfigurer
  • @PropertySource
    在类上加这个@PropertySource注解,就可以配置配置的来源,将property文件配置在@PropertySource注解里面,也可以在里面添加如果找不到这个配置项 之后 的做法
  • @PropertySources
    如果有多个@PropertySource,在类上添加@PropertySources 的集合,是多个@PropertySource的注解

Spring Boot 中的 @ConfigurationProperties

  • 可以将属性绑定到结构化对象上

  • 支持 Relaxed Binding

  • 支持安全的类型转换

  • 在配置类上加上@EnableConfigurationProperties 注解,这个注解就可以开启@ConfigurationProperties注解的支持

以JdbcProperties源码为例:

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;

@ConfigurationProperties(
    prefix = "spring.jdbc"
)//通过prefix 说明所有的spring.jdbc打头的属性 都会被绑定到JdbcProperties里面 
public class JdbcProperties {
    private final JdbcProperties.Template template = new JdbcProperties.Template();
    //结构化就是通过对象进行属性的绑定  比如说 spring.jdbc.template 打头的这些属性会被绑定到Template 里面的这些属性上的  比如说 spring.jdbc.template.fetchSize 都会绑定到fetchSize 上
    public JdbcProperties() {
    }

    public JdbcProperties.Template getTemplate() {
        return this.template;
    }

    public static class Template {
        private int fetchSize = -1;
        private int maxRows = -1;
        @DurationUnit(ChronoUnit.SECONDS) //支持安全的类型转换 只要是Unit支持的 就可以写上去
        private Duration queryTimeout;

        public Template() {
        }

        public int getFetchSize() {
            return this.fetchSize;
        }

        public void setFetchSize(int fetchSize) {
            this.fetchSize = fetchSize;
        }

        public int getMaxRows() {
            return this.maxRows;
        }

        public void setMaxRows(int maxRows) {
            this.maxRows = maxRows;
        }

        public Duration getQueryTimeout() {
            return this.queryTimeout;
        }

        public void setQueryTimeout(Duration queryTimeout) {
            this.queryTimeout = queryTimeout;
        }
    }
}

定制 PropertySource

主要步骤

  • 实现 PropertySource<T>的bean
  • 把 PropertySource注册到EnvironmentPropertySource 从 Environment 取得 PropertySources
  • 将自己的 PropertySource添加到合适的位置
    切入位置
  • EnvironmentPostProcessor(springboot)
  • BeanFactoryPostProcessor (spring)

以源码RandomValuePropertySource为例:

public class RandomValuePropertySource extends PropertySource<Random> {//提供随机数的PropertySource 实现 PropertySource<T<T>>的bean
    public static final String RANDOM_PROPERTY_SOURCE_NAME = "random";
    private static final String PREFIX = "random.";
    private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class);

    public RandomValuePropertySource(String name) {
        super(name, new Random());//使用父类构造方法 传入一个名字 一个Source 一个Random对象
    }

    public RandomValuePropertySource() {
        this("random");
    }

    public Object getProperty(String name) {
        if (!name.startsWith("random.")) {//判断是否以random.打头
            return null;
        } else {
            if (logger.isTraceEnabled()) {
                logger.trace("Generating random property for '" + name + "'");
            }

            return this.getRandomValue(name.substring("random.".length()));//去掉random. 截取后面的一段
        }
    }

    private Object getRandomValue(String type) {
        if (type.equals("int")) {//如果是int类型的
            return ((Random)this.getSource()).nextInt();//在getSource()中取下一个int
        } else if (type.equals("long")) {
            return ((Random)this.getSource()).nextLong();
        } else {
            String range = this.getRange(type, "int");//也可以是一个范围  使用getRange取得一个范围
            if (range != null) {
                return this.getNextIntInRange(range);
            } else {
                range = this.getRange(type, "long");
                if (range != null) {
                    return this.getNextLongInRange(range);
                } else {
                    return type.equals("uuid") ? UUID.randomUUID().toString() : this.getRandomBytes();
                }
            }
        }
    }

    private String getRange(String type, String prefix) {
        if (type.startsWith(prefix)) {
            int startIndex = prefix.length() + 1;
            if (type.length() > startIndex) {
                return type.substring(startIndex, type.length() - 1);
            }
        }

        return null;
    }

    private int getNextIntInRange(String range) {
        String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
        int start = Integer.parseInt(tokens[0]);
        return tokens.length == 1 ? ((Random)this.getSource()).nextInt(start) : start + ((Random)this.getSource()).nextInt(Integer.parseInt(tokens[1]) - start);
    }

    private long getNextLongInRange(String range) {
        String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
        if (tokens.length == 1) {
            return Math.abs(((Random)this.getSource()).nextLong() % Long.parseLong(tokens[0]));
        } else {
            long lowerBound = Long.parseLong(tokens[0]);
            long upperBound = Long.parseLong(tokens[1]) - lowerBound;
            return lowerBound + Math.abs(((Random)this.getSource()).nextLong() % upperBound);
        }
    } 

    private Object getRandomBytes() {
        byte[] bytes = new byte[32];
        ((Random)this.getSource()).nextBytes(bytes);
        return DigestUtils.md5DigestAsHex(bytes);
    }

    public static void addToEnvironment(ConfigurableEnvironment environment) {
        environment.getPropertySources().addAfter("systemEnvironment", new RandomValuePropertySource("random"));
        logger.trace("RandomValuePropertySource add to Environment");
    }
}

例子

@SpringBootApplication
@Slf4j
public class PropertySourceDemoApplication implements ApplicationRunner {
	@Value("${geektime.greeting}")//从配置文件中取得
	private String greeting;

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

	@Override
	public void run(ApplicationArguments args) throws Exception {
		log.info("{}", greeting);
	}
}
@Slf4j
public class YapfEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader();
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();//从 Environment 取得 PropertySources
        Resource resource = new ClassPathResource("yapf.properties");//创建一个自己的PropertySource
        try {
            PropertySource ps = loader.load("YetAnotherPropertiesFile", resource)//通过PropertiesPropertySourceLoader 去load一个resource
                    .get(0);
            propertySources.addFirst(ps); //添加到第一个位置上 即 完成yapf.properties的注册
        } catch (Exception e) {
            log.error("Exception!", e);
        }
    }
}

spring.factories

org.springframework.boot.env.EnvironmentPostProcessor=spring.hello.YapfEnvironmentPostProcessor

yapf.properties

geektime.greeting=hello

结果

在这里插入图片描述

在控制台打印出hello

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值