PropertySource 类

本文将学习 Spring 的 PropertySource、EnumerablePropertySource 和 CommandLinePropertySource 类

PropertySource 基础抽象类表示 name/value 属性对源,基础源对象可以是封装属性的任何类型 T。 示例包括 Properties 对象,Map 对象,ServletContext 和 ServletConfig 对象(用于访问初始化参数)。 探索 PropertySource 类型层次结构以查看提供的实现。

PropertySource 对象通常不是孤立使用的,而是通过 PropertySources 对象使用的,该对象聚合属性源并与 PropertyResolver 实现结合使用,该实现可以对整个 PropertySources 执行基于优先级的搜索。

PropertySource 身份的确定不是基于封装属性的内容,而是仅基于 PropertySource 的名称。 这对于在集合上下文中操作PropertySource 对象很有用。 有关详细信息,请参见 MutablePropertySources 中的操作以及 named(String) 和 toString() 方法。

请注意,在使用 @Configuration 类时,@PropertySource 批注提供了一种方便的声明性方式,可以将属性源添加到封闭环境中。

EnumerablePropertySource 类

该类是 PropertySource 的实现,能够询问其基础源对象来枚举所有可能的属性名称/值对。暴露 getPropertyNames() 方法,以使调用者可以内省可用属性,而不必访问基础源对象。这还有助于更有效地实现 containsProperty(String),因为它可以调用 getPropertyNames() 并遍历返回的数组,而不是尝试调用可能更昂贵的 PropertySource.getProperty(String)。实现可以考虑缓存 getPropertyNames() 的结果以充分利用此性能机会。

多数框架提供的 PropertySource 实现都是可枚举的。一个反例是 JndiPropertySource,由于 JNDI 的性质,在任何给定时间不可能确定所有可能的属性名称;而是只能尝试访问属性(通过 PropertySource.getProperty(String))以评估它是否存在。

CommandLinePropertySource 类

由命令行参数支持的 PropertySource 实现的抽象基类。参数化类型 T 表示命令行选项的基础源。对于SimpleCommandLinePropertySource,它可以像 String 数组一样简单,或者对于 JoptCommandLinePropertySource,则特定于特定的API,例如 JOpt 的 OptionSet。

目的和一般用途

用于基于独立的 Spring 应用程序,即那些通过传统的 main 方法引导的应用程序,这些应用程序接受命令行中的参数 String[]。在许多情况下,直接在 main 方法内处理命令行参数就足够了,但是在其他情况下,可能需要将参数作为值注入到 Spring bean 中。在后者的情况下,CommandLinePropertySource 变得有用。通常会将 CommandLinePropertySource 添加到 Spring ApplicationContext 的环境中。此时,所有命令行参数都可以通过 PropertyResolver.getProperty(String) 方法使用。例如:

public static void main(String[] args) {
	CommandLinePropertySource clps = ...;
	AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
	ctx.getEnvironment().getPropertySources().addFirst(clps);
	ctx.register(AppConfig.class);
	ctx.refresh();
}

使用上面的引导逻辑,AppConfig 类可以 @Inject Spring Environment 并直接向其查询属性:

@Configuration
public class AppConfig {
	@Inject
	private Environment env;

	@Bean
	public void DataSource dataSource() {
		MyVendorDataSource dataSource = new MyVendorDataSource();
		dataSource.setHostname(env.getProperty("db.hostname", "localhost"));
		dataSource.setUsername(env.getRequiredProperty("db.username"));
		dataSource.setPassword(env.getRequiredProperty("db.password"));
		// ...
		return dataSource;
	}
}

因为 CommandLinePropertySource 是使用 addFirst() 方法添加到环境的 MutablePropertySources 集中的,所以它具有最高的搜索优先级,这意味着,虽然 “db.hostname” 和其他属性可能存在于其他属性源(例如系统环境变量)中,但它将首先从命令行属性源中选择。

作为注入 Environment 的替代方法,假定已直接或通过使用 <context:property-placeholder> 元素注册了 PropertySourcesPropertyResolver Bean,则可以使用 Spring 的 @Value 注解注入这些属性。

@Component
public class MyComponent {
	@Value("my.property:defaultVal")
	private String myProperty;
	
	public void getMyProperty() {
		return this.myProperty;
	}
	// ...
}

使用选项参数

各个命令行参数通过常用的 PropertySource.getProperty(String) 和 PropertySource.containsProperty(String) 方法表示为属性。 例如,给定以下命令行:

--o1=v1 --o2

“o1” 和 “o2” 被视为 “选项参数”,并且以下断言将为 true:

CommandLinePropertySource ps = ...
assert ps.containsProperty("o1") == true;
assert ps.containsProperty("o2") == true;
assert ps.containsProperty("o3") == false;
assert ps.getProperty("o1").equals("v1");
assert ps.getProperty("o2").equals("");
assert ps.getProperty("o3") == null;

使用非选项参数

此抽象还支持非选项参数。任何没有选项式前缀(如 “-” 或 “--”)的参数都被认为是 “非选项参数”,并且可以通过特殊的 “non-OptionArgs” 属性获得。如果指定了多个非选项参数,则此属性的值将是包含所有参数的逗号分隔字符串。这种方法可确保CommandLinePropertySource中所有属性的返回类型简单且一致,并且与Spring Environment及其内置的ConversionService结合使用时,可以很容易地进行转换。考虑以下示例:

--o1=v1 --o2=v2 /path/to/file1 /path/to/file2

在此示例中,“o1” 和 “o2” 将被视为 “选项参数”,而两个文件系统路径则被视为 “非选项参数”。因此,以下断言将为 true:

CommandLinePropertySource ps = ...
assert ps.containsProperty("o1") == true;
assert ps.containsProperty("o2") == true;
assert ps.containsProperty("nonOptionArgs") == true;
assert ps.getProperty("o1").equals("v1");
assert ps.getProperty("o2").equals("v2");
assert ps.getProperty("nonOptionArgs").equals("/path/to/file1,/path/to/file2");

如上所述,当与 Spring Environment 抽象结合使用时,此逗号分隔的字符串可以轻松转换为 String 数组或列表:

Environment env = applicationContext.getEnvironment();
String[] nonOptionArgs = env.getProperty("nonOptionArgs", String[].class);
assert nonOptionArgs[0].equals("/path/to/file1");
assert nonOptionArgs[1].equals("/path/to/file2");

特殊的 “非选项参数” 属性的名称可以通过 setNonOptionArgsPropertyName(String) 方法进行自定义。建议这样做,因为它可以为非选项参数提供适当的语义值。例如,如果将文件系统路径指定为非选项参数,则可能更喜欢将其称为 “file.locations”,而不是默认的 “nonOptionArgs”:

public static void main(String[] args) {
	CommandLinePropertySource clps = ...;
	clps.setNonOptionArgsPropertyName("file.locations");

	AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
	ctx.getEnvironment().getPropertySources().addFirst(clps);
	ctx.register(AppConfig.class);
	ctx.refresh();
}

局限性

此抽象无意于展现底层命令行解析 API(例如 JOpt 或 Commons CLI)的全部功能。 它的意图恰恰相反:提供对解析后的命令行参数进行访问的最简单抽象。 因此,典型情况将涉及到完全配置基础命令行解析 API,解析进入 main 方法的参数的 String [],然后简单地将解析结果提供给 CommandLinePropertySource 的实现。在这一点上,所有参数都可以视为 “选项” 或 “非选项” 参数,并且如上所述,可以通过常规的 PropertySource 和 Environment API 进行访问。

游手好闲地学习,并不比学习游手好闲好。 —— 约翰·贝勒斯
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号