Spring Boot通过配置获取随机值

Posted by Jeremy Song on 2021-08-09
Estimated Reading Time 7 Minutes
Words 1.5k In Total
Viewed Times

如果您想在Spring Boot项目的配置文件(application.propertiesapplication.yml)中设置一个随机值,应该怎么办呢?这个Spring Boot已经有了解决方案。下面就通过示例详细说明。

配置随机值

在Spring Boot项目中同构配置获取随机值可以参考如下配置示例:

  • application.properties
1
2
3
4
5
6
7
my.id=${random.uuid}
my.secret="${random.value}"
my.number="${random.int}"
my.bignumber="${random.long}"
my.uuid="${random.uuid}"
my.number-less-than-ten="${random.int(10)}"
my.number-in-range="${random.int[1024,65536]}"
  • application.yml
1
2
3
4
5
6
7
8
my:
id: ${random.uuid}
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}"

OK!有了上面的配置,我们来用如下代码测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class RandomValuesTester {

@Value("${my.id}")
private String id;
@Value("${my.secret}")
private String secret;
@Value("${my.number}")
private String number;
@Value("${my.bignumber}")
private String bigNumber;
@Value("${my.uuid}")
private String uuid;
@Value("${my.number-less-than-ten}")
private String numberLessThanTen;
@Value("${my.number-in-range}")
private String numberInRange;

@Autowired
private Environment environment;

public void test() {
placeholderTest();
System.out.println("========================");
useEnvironmentTest();
}

private void placeholderTest() {
System.out.println(id);
System.out.println(secret);
System.out.println(number);
System.out.println(bigNumber);
System.out.println(uuid);
System.out.println(numberLessThanTen);
System.out.println(numberInRange);
}

private void useEnvironmentTest() {
System.out.println(environment.getProperty("my.id"));
System.out.println(environment.getProperty("my.secret"));
System.out.println(environment.getProperty("my.number"));
System.out.println(environment.getProperty("my.bignumber"));
System.out.println(environment.getProperty("my.uuid"));
System.out.println(environment.getProperty("my.number-less-than-ten"));
System.out.println(environment.getProperty("my.number-in-range"));
}
}

来运行一下上面的测试,看看结果如何:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2021-03-31 19:45:57.632  INFO 25308 --- [           main] com.jeremy.tech.lab.LabApplication       : Started LabApplication in 2.034 seconds (JVM running for 3.125)
43adc303-aa7b-4424-ac59-4261b7527461
4032da51f1edd76256abee1683c6ca96
-354560558
-6325973753148526700
04e686db-4a1a-42af-8d59-1d17501166ae
8
35928
========================
258a9bb9-f6fc-4d5b-bcea-8a4528a60c6d
04e44548ea9a5c5b2c0bee1a4f2c1658
-722507177
-313282001081326805
509dba0b-caf2-4477-8970-de648baa388e
8
51141
2021-03-31 19:45:58.158 INFO 25308 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'

怎么样?你可以多运行几次(可以循环调用 environment.getProperty("xxx"))看看是不是每次都是随机的值。下面介绍一下这些值是怎么获取的。

源码分析

其实这个的实现并不神秘,一切都靠 org.springframework.boot.env.RandomValuePropertySource 这个类来实现。

这个类的源码如下(关键部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class RandomValuePropertySource extends PropertySource<Random> {

/**
* Name of the random {@link PropertySource}.
*/
public static final String RANDOM_PROPERTY_SOURCE_NAME = "random";

private static final String PREFIX = "random.";

public RandomValuePropertySource(String name) {
super(name, new Random());
}

@Override
public Object getProperty(String name) {
if (!name.startsWith(PREFIX)) {
return null;
}
logger.trace(LogMessage.format("Generating random property for '%s'", name));
return getRandomValue(name.substring(PREFIX.length()));
}

private Object getRandomValue(String type) {
if (type.equals("int")) {
return getSource().nextInt();
}
if (type.equals("long")) {
return getSource().nextLong();
}
String range = getRange(type, "int");
if (range != null) {
return getNextIntInRange(range);
}
range = getRange(type, "long");
if (range != null) {
return getNextLongInRange(range);
}
if (type.equals("uuid")) {
return UUID.randomUUID().toString();
}
return 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]);
if (tokens.length == 1) {
return getSource().nextInt(start);
}
return start + getSource().nextInt(Integer.parseInt(tokens[1]) - start);
}

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

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

}

分析

通过上面的源码,我们可以很容易的识别出来我们是如何获取这些随机值的。原来这些随机值都是通过 Object getProperty(String name) 这个函数来获取的,这个函数是继承了父类的重写方法。

通过函数调用 Object getRandomValue(String type) 可以很清楚的看到每种随机值的获取方式。源码很简单我就不展开说明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private Object getRandomValue(String type) {
if (type.equals("int")) {
return getSource().nextInt();
}
if (type.equals("long")) {
return getSource().nextLong();
}
String range = getRange(type, "int");
if (range != null) {
return getNextIntInRange(range);
}
range = getRange(type, "long");
if (range != null) {
return getNextLongInRange(range);
}
if (type.equals("uuid")) {
return UUID.randomUUID().toString();
}
return getRandomBytes();
}

结论

通过分析我们可以得出结论:

  • ${random.value} 的值是一个随机的32位十六进制的MD5摘要值
  • ${random.int} 的值是通过 new Random() 获取的一个随机的int值
  • ${random.long}的值是通过 new Random() 获取的一个随机的long值
  • ${random.uuid} 的值是通过 UUID.randomUUID().toString() 获取的一个随机UUID值
  • ${random.int(10)} 的值是一个不大于10的int值
  • ${random.int[1024,65536]} 的值是一个包含1024,不包含65535的一个随机值

注:${random.int[min,max]} 中的 [min, max] 是一个包含min不包含max的范围,{random.long[min,max]} 也是一样的,只不过最大值是Long.MAX 而已。

最后

Spring Boot的属性配置也是一个比较大的知识点,这里仅仅列出了冰山一角。如果您想了解更多的关于配置的概念,可以从了解PropertySource 入手,属性的初始化在 Application.run 函数中的如下三个关键函数调用中赋值:

  • prepareContext
  • refreshContext
  • afterRefresh

另外,顺便提一句。上文一开始给出了两种配置文件 *.properties*.yml 。 如果您的项目中同时存在这两个配置文件,并且配置文件中的属性有同样的key,最终 *.yml 中的配置会覆盖 *.properties 中的配置。


欢迎关注我的公众号 须弥零一,跟我一起学习IT知识。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !