知識改變命運(yùn),擼碼使我快樂,2020繼續(xù)游走在開源界
點(diǎn)贊再看,養(yǎng)成習(xí)慣
給我來個(gè)Star吧翅帜,點(diǎn)擊了解下基于SpringBoot的組件化接口服務(wù)落地解決方案
隨機(jī)數(shù)的使用你是不是經(jīng)常用到?我們在進(jìn)行運(yùn)行SpringBoot
單元測試時(shí)一般不會指定應(yīng)用程序啟動時(shí)的端口號
命满,可以在application.properties
文件內(nèi)配置server.port
的值為${random.int(10000)}
涝滴,代表了隨機(jī)使用0~10000
的端口號。
既然這種方式使用這么方便胶台,那你知道${random.int}
是通過什么方式實(shí)現(xiàn)的嗎歼疮?
推薦閱讀
概述
配置文件方式
在我們分析源碼之前,我們先來看看${random.xxx}
具體提供了哪幾種的隨機(jī)配置诈唬。
int隨機(jī)數(shù)
使用${random.int}
方式配置韩脏,結(jié)果從int
的最大值、最小值中間產(chǎn)生讯榕,int
的最小值為-2147483648
匙睹,最大值為2147483647
,配置如下所示:
server:
port: ${random.int}
int范圍隨機(jī)數(shù)
使用${random.int(10000)}
方式配置痕檬,這種方式我們可以指定隨機(jī)數(shù)的最大值,當(dāng)然不能超過2147483647
梦谜,配置如下所示:
server:
port: ${random.int(10000)}
注意事項(xiàng):
${random.int(10000)}
隨機(jī)數(shù)的值將會在0~10000
之間產(chǎn)生丘跌,配置的最大值必須為正整數(shù)O啊1帐鳌!如果需要指定隨機(jī)數(shù)的最小值报辱,可以使用
${random.int[100,200]}
方式配置与殃,這樣只會從100~200
之間產(chǎn)生隨機(jī)數(shù)(包括最小值,不包括最大值)碍现。
long隨機(jī)數(shù)
使用${random.long}
方式配置幅疼,結(jié)果會從long
的最大值昼接、最小值中間產(chǎn)生,long
的最小值為-9223372036854775808
逐工,最大值為9223372036854775807
一睁,配置方式如下所示:
config:
longValue: ${random.long}
long范圍隨機(jī)數(shù)
使用${random.long(10000)}
方式配置,我們可以指定0~9223372036854775807
之間的任意數(shù)值作為隨機(jī)的最大上限者吁,配置方式如下所示:
config:
maxLongValue: ${random.long(102400)}
uuid隨機(jī)數(shù)
uuid
因?yàn)樗奈ㄒ恍裕瑧?yīng)該是我們平時(shí)開發(fā)中比較常用到的复凳。
SpringBoot
也為我們考慮到了這一點(diǎn),我們只需要使用${random.uuid}
就可以獲得一個(gè)隨機(jī)的uuid
字符串对途,配置方式如下所示:
config:
uuid: ${random.uuid}
@Value方式
如果在我們在編碼中需要用到隨機(jī)數(shù)的生成髓棋,${random}
是支持注入使用的,主要還是因?yàn)樗膶?shí)現(xiàn)繼承自PropertySource
按声。
我們可以在Spring IOC
所管理的類內(nèi)直接使用@Value
注解進(jìn)行注入使用签则,如下所示:
/**
* 隨機(jī)生成uuid字符串
*/
@Value("${random.uuid}")
private String uuid;
/**
* 隨機(jī)生成0~1000的正整數(shù)
*/
@Value("${random.int(1000)}")
private int maxInt;
/**
* 隨機(jī)生成0~102400的long類型數(shù)值
*/
@Value("${random.long(102400)}")
private long maxLong;
源碼解析
我們之所以可以這么方便的使用隨機(jī)數(shù),都?xì)w功于SpringBoot
為我們提供了一個(gè)名為RandomValuePropertySource
的PropertySource
實(shí)現(xiàn)類渐裂,該實(shí)現(xiàn)類位于org.springframework.boot.env
包內(nèi)钠惩,該類部分源碼如下所示:
/**
* {@link PropertySource} that returns a random value for any property that starts with
* {@literal "random."}. Where the "unqualified property name" is the portion of the
* requested property name beyond the "random." prefix, this {@link PropertySource}
* ...
*/
public class RandomValuePropertySource extends PropertySource<Random> {
private static final String PREFIX = "random.";
private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class);
@Override
public Object getProperty(String name) {
// 僅處理random.開頭的配置
if (!name.startsWith(PREFIX)) {
return null;
}
if (logger.isTraceEnabled()) {
logger.trace("Generating random property for '" + name + "'");
}
// 獲取數(shù)據(jù)數(shù)族阅,將random.后的內(nèi)容作為類型參數(shù)傳遞到getRandomValue方法
return getRandomValue(name.substring(PREFIX.length()));
}
private Object getRandomValue(String type) {
// 處理random.int類型的隨機(jī)數(shù)
if (type.equals("int")) {
return getSource().nextInt();
}
// 處理random.long類型的隨機(jī)數(shù)
if (type.equals("long")) {
return getSource().nextLong();
}
// 處理random.int(100)類型的隨機(jī)數(shù)
String range = getRange(type, "int");
if (range != null) {
// 生成有范圍的int類型隨機(jī)數(shù)
return getNextIntInRange(range);
}
// 處理random.long(1024)類型的隨機(jī)數(shù)
range = getRange(type, "long");
if (range != null) {
// 生成有范圍的long類型隨機(jī)數(shù)
return getNextLongInRange(range);
}
// 處理random.uuid類型的隨機(jī)數(shù)
if (type.equals("uuid")) {
// 生成隨機(jī)的uuid返回
return UUID.randomUUID().toString();
}
// 默認(rèn)返回隨機(jī)字節(jié)
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);
}
}
當(dāng)我們使用${random.xxx}
這種方式獲取隨機(jī)數(shù)時(shí)举塔,無論是配置文件
方式還是@Value
方式都會通過org.springframework.boot.env.RandomValuePropertySource#getProperty
方法來獲取對應(yīng)類型的隨機(jī)數(shù)求泰。
注意事項(xiàng):
RandomValuePropertySource
在繼承PropertySource
時(shí)泛型類型為Random
,java.util.Random
類內(nèi)包含了全部的隨機(jī)生成邏輯渴频,該類由java
提供,有興趣可以研究下源碼拔第。
總結(jié)
SpringBoot
內(nèi)的配置都是通過ConfigurablePropertyResolver
屬性配置解析器來獲取的场钉,而該類的實(shí)例化在AbstractEnvironment
內(nèi),我們通過AbstractEnvironment#getProperty(java.lang.String)
方法可以獲取由多個(gè)PropertySource
實(shí)現(xiàn)類提供的屬性配置逛万。