【Spring】PropertySource 的解读与示例(MapPropertySource CommandLinePropertySource)

前言

之前写过一篇解读 PropertySource 源码的文章,本文做一个补充,重点解读下 CommandLinePropertySource,并添加部分示例

【源码】Spring —— PropertySource 解读

PropertySource

public abstract class PropertySource<T> {

	protected final String name;

	protected final T source;

	// 	...

}

顶层抽象类,以 name 维度管理资源 source,并提供如下方法

	public boolean containsProperty(String name) {
		return (getProperty(name) != null);
	}

	@Nullable
	public abstract Object getProperty(String name);

EnumerablePropertySource

public abstract class EnumerablePropertySource<T> extends PropertySource<T> {

	public EnumerablePropertySource(String name, T source) {
		super(name, source);
	}

	protected EnumerablePropertySource(String name) {
		super(name);
	}

	@Override
	public boolean containsProperty(String name) {
		return ObjectUtils.containsElement(getPropertyNames(), name);
	}

	public abstract String[] getPropertyNames();

}

PropertySource 的基础上拓展了 枚举 能力,提供 getPropertyNames 获取所有属性名称,因而 containsProperty 方法也基于此实现

继承图
EnumerablePropertySource 是个核心分支,其下:

  • DynamicValuesPropertySourcetest 模块下的非公开类,用途未知,维护的 source 是一个 Map<String, Supplier<Object>> valueSuppliers,其中的 Supplier 用来生成属性 k 对应的值
  • ServletConfigPropertySourceServletContextPropertySource 都是 web 模块下维护对应 ServletConfigServletContext 类型的 source,用于 web environment
  • MapPropertySource,最常用的分支,source 类型为 Map,下文重点解读
  • CommandLinePropertySource,将 命令行参数 维护成对应的 source,我们在启动 SpringBoot 项目时用到的 命令行参数 就被封装成 CommandLinePropertySource,下文重点解读
  • CompositePropertySource,组合模式,可以组合上述的 EnumerablePropertySource

MapPropertySource

其维护的 source 类型为 Map<String, Object>,因而对应的方法基于 Map 实现,比如:

	@Override
	@Nullable
	public Object getProperty(String name) {
		return this.source.get(name);
	}

	@Override
	public boolean containsProperty(String name) {
		return this.source.containsKey(name);
	}

	@Override
	public String[] getPropertyNames() {
		return StringUtils.toStringArray(this.source.keySet());
	}

展示一段示例 demo

	@Test
    public void map() {
        Map<String, Object> map = new HashMap<>() {
            {
                put("a", "1");
                put("b", "2");
            }
        };

        MapPropertySource mapPropertySource
                = new MapPropertySource("source", map);

        Assertions.assertEquals("1", mapPropertySource.getProperty("a"));

    }

SystemEnvironmentPropertySource

SystemEnvironmentPropertySource 继承了 MapPropertySource,主要用来维护 系统环境参数,在 MapPropertySource 的基础上,SystemEnvironmentPropertySource 忽略属性 key 的大小写,同时会在不存在对应属性是,先后试图处理 . - 等特殊符号后再次获取

展示一段 SystemEnvironmentPropertySource 的示例 demo

	@Test
    public void system() {
        Map<String, Object> map = new HashMap<>() {
            {
                put("a_b", "1");
                put("C_D", "2");
            }
        };

        SystemEnvironmentPropertySource propertySource
                = new SystemEnvironmentPropertySource("system", map);

        Assertions.assertEquals("1", propertySource.getProperty("a.b"));
        Assertions.assertEquals("2", propertySource.getProperty("c-d"));
    }

PropertiesPropertySource

PropertiesPropertySource 继承 MapPropertySource,接受 Properties 类型的 source,毕竟 Properties 就是 Map 的子类

CommandLinePropertySource

我们在启动 SpringBoot 项目时,是可以指定 命令行参数 的,诸如:

java -jar xxx.jar --server.port=8080

通过 命令行参数 我们可以干预 SpringApplication 及其 Environment 的属性,实际上命令行参数被解析成了 CommandLinePropertySource(本文重点关注 SimpleCommandLinePropertySource),同时它拥有极高的优先权

命令行参数 分为两类:

  • option arguments:指定方式通常如下 --k=v
  • non-option arguments:不以诸如 -- 之类前缀修饰的属性,被解析为 non-option arguments,诸如 a b c 会被解析为 a,b,c,其 keynonOptionArgs,当然我们也可以通过 setNonOptionArgsPropertyName 方法修改该 key

Spring 针对 CommandLinePropertySource 提供了两个实现(当然我们也可以自己实现),分别为 SimpleCommandLinePropertySourceJOptCommandLinePropertySource,基本我们用到的也就是 SimpleCommandLinePropertySource

SimpleCommandLinePropertySource

在该类中,命令行参数 被解析为 CommandLineArgs,负责解析工作的类是 SimpleCommandLineArgsParser,见其构造方法:

public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {

	public SimpleCommandLinePropertySource(String... args) {
		super(new SimpleCommandLineArgsParser().parse(args));
	}

	// 	...

}

对应的属性操作也直接委托给了 CommandLineArgsCommandLineArgsSimpleCommandLineArgsParser 的代码较为简单,不再深入

给出一段示例 demo

	@Test
    public void command() {
        String[] command = { "--o1=v1", "--o2", "a", "b", "c" };
        SimpleCommandLinePropertySource propertySource
                = new SimpleCommandLinePropertySource("command", command);

        Assertions.assertEquals("v1", propertySource.getProperty("o1"));
        
        // 可以不指定值,结果为 ""
        Assertions.assertEquals("", propertySource.getProperty("o2"));
        
        // 不指定 key,则值为 null
        Assertions.assertNull(propertySource.getProperty("o3"));
        
        // nonOptionArgs 会用 , 连接返回
        Assertions.assertEquals("a,b,c", propertySource.getProperty("nonOptionArgs"));
    }

PropertySources

public interface PropertySources extends Iterable<PropertySource<?>> {

	default Stream<PropertySource<?>> stream() {
		return StreamSupport.stream(spliterator(), false);
	}
	
	boolean contains(String name);

	@Nullable
	PropertySource<?> get(String name);

}

维护一组 PropertySource 集合,定义了 方法,唯一子类 MutablePropertySources 额外提供了 方法

mutable

MutablePropertySources

MutablePropertySources 基于 CopyOnWriteArrayList 维护一组 PropertySource

	private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();

它提供了 addFirst addLast addBefore addAfter 等方法,允许细粒度的控制 PropertySource 的优先级,Environment 就是借助它来维护对应的 属性集,并遵循严格的优先级

给出一段示例 demo

	@Test
    public void mutable() {
        MutablePropertySources propertySources = new MutablePropertySources();

        Map<String, Object> map = new HashMap<>() {
            {
                put("a", "1");
                put("b", "2");
            }
        };
        MapPropertySource mapPropertySource
                = new MapPropertySource("map", map);

        String command = "--a=3";
        SimpleCommandLinePropertySource commandLinePropertySource
                = new SimpleCommandLinePropertySource("command", command);

        StandardEnvironment environment = new StandardEnvironment();
        environment
                .getPropertySources()
                .addFirst(mapPropertySource);
        Assertions.assertEquals("1", environment.getProperty("a"));

        // 插到 mapPropertySource 前面
        environment
                .getPropertySources()
                .addBefore(
                        "map"
                        , commandLinePropertySource
                );
        Assertions.assertEquals("3", environment.getProperty("a"));
    }

这里我们借助 StandardEnvironment 来维护、获取属性,其中 environment.getPropertySources() 返回的就是一个 MutablePropertySources ,可以看到 addBefore 方法让后者覆盖了前者

总结

PropertySource 相关解读到此为止,Spring 广泛使用的类,希望自己在平时的开发中也可以使用到~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值