SpringBoot2.x基礎(chǔ)篇:探索配置文件中隨機(jī)數(shù)的實(shí)現(xiàn)方式

知識改變命運(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è)名為RandomValuePropertySourcePropertySource實(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í)泛型類型為Randomjava.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)類提供的屬性配置逛万。

作者個(gè)人 博客
使用開源框架 ApiBoot 助你成為Api接口服務(wù)架構(gòu)師

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宇植,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子指郁,更是在濱河造成了極大的恐慌,老刑警劉巖疫粥,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腰懂,死亡現(xiàn)場離奇詭異,居然都是意外死亡悯恍,警方通過查閱死者的電腦和手機(jī)伙狐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罢防,“玉大人,你說我怎么就攤上這事野建。” “怎么了候生?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵绽昼,是天一觀的道長。 經(jīng)常有香客問我目溉,道長菱农,這世上最難降的妖魔是什么缭付? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任陷猫,我火速辦了婚禮,結(jié)果婚禮上只厘,老公的妹妹穿的比我還像新娘。我一直安慰自己河咽,他們只是感情好赋元,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著媚值,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褥芒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天献酗,我揣著相機(jī)與錄音,去河邊找鬼罕偎。 笑死京闰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蹂楣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼乾翔,長吁一口氣:“原來是場噩夢啊……” “哼施戴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赞哗,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肪笋,失蹤者是張志新(化名)和其女友劉穎月劈,沒想到半個(gè)月后藤乙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡而姐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年拴念,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了褐缠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片政鼠。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡公般,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出官帘,到底是詐尸還是另有隱情,我是刑警寧澤遏佣,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布揽浙,位于F島的核電站,受9級特大地震影響膛虫,放射性物質(zhì)發(fā)生泄漏钓猬。R本人自食惡果不足惜稍刀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一敞曹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧澳迫,春花似錦、人聲如沸橄登。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卒稳。三九已至,卻和暖如春充坑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匪傍。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工役衡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茵休,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓俐芯,卻偏偏與公主長得像钉鸯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子唠雕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345