SpringBoot2.x基础篇:探索配置文件中随机数的实现方式

知识改变命运,撸码使我快乐,2020继续游走在开源界

点赞再看,养成习惯

给我来个Star吧,点击了解下基于SpringBoot的组件化接口服务落地解决方案

随机数的使用你是不是经常用到?我们在进行运行SpringBoot单元测试时一般不会指定应用程序启动时的端口号,可以在application.properties文件内配置server.port的值为${random.int(10000)},代表了随机使用0~10000的端口号。

既然这种方式使用这么方便,那你知道${random.int}是通过什么方式实现的吗?

推荐阅读

概述

配置文件方式

在我们分析源码之前,我们先来看看${random.xxx}具体提供了哪几种的随机配置。

int随机数

使用${random.int}方式配置,结果从int的最大值、最小值中间产生,int的最小值为-2147483648,最大值为2147483647,配置如下所示:

server:
  port: ${random.int}

int范围随机数

使用${random.int(10000)}方式配置,这种方式我们可以指定随机数的最大值,当然不能超过2147483647,配置如下所示:

server:
  port: ${random.int(10000)}

注意事项:${random.int(10000)}随机数的值将会在0~10000之间产生,配置的最大值必须为正整数!!!

如果需要指定随机数的最小值,可以使用${random.int[100,200]}方式配置,这样只会从100~200之间产生随机数(包括最小值,不包括最大值)。

long随机数

使用${random.long}方式配置,结果会从long的最大值、最小值中间产生,long的最小值为-9223372036854775808,最大值为9223372036854775807,配置方式如下所示:

config:
  longValue: ${random.long}

long范围随机数

使用${random.long(10000)}方式配置,我们可以指定0~9223372036854775807之间的任意数值作为随机的最大上限,配置方式如下所示:

config:
  maxLongValue: ${random.long(102400)}

uuid随机数

uuid因为它的唯一性,应该是我们平时开发中比较常用到的。

SpringBoot也为我们考虑到了这一点,我们只需要使用${random.uuid}就可以获得一个随机的uuid字符串,配置方式如下所示:

config:
  uuid: ${random.uuid}

@Value方式

如果在我们在编码中需要用到随机数的生成,${random}是支持注入使用的,主要还是因为它的实现继承自PropertySource

我们可以在Spring IOC所管理的类内直接使用@Value注解进行注入使用,如下所示:

/**
 * 随机生成uuid字符串
 */
@Value("${random.uuid}")
private String uuid;
/**
 * 随机生成0~1000的正整数
 */
@Value("${random.int(1000)}")
private int maxInt;
/**
 * 随机生成0~102400的long类型数值
 */
@Value("${random.long(102400)}")
private long maxLong;

源码解析

我们之所以可以这么方便的使用随机数,都归功于SpringBoot为我们提供了一个名为RandomValuePropertySourcePropertySource实现类,该实现类位于org.springframework.boot.env包内,该类部分源码如下所示:

/**
 * {@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 + "'");
    }
    // 获取数据数,将random.后的内容作为类型参数传递到getRandomValue方法
    return getRandomValue(name.substring(PREFIX.length()));
  }
  
  private Object getRandomValue(String type) {
    // 处理random.int类型的随机数
    if (type.equals("int")) {
      return getSource().nextInt();
    }
    // 处理random.long类型的随机数
    if (type.equals("long")) {
      return getSource().nextLong();
    }
    // 处理random.int(100)类型的随机数
    String range = getRange(type, "int");
    if (range != null) {
      // 生成有范围的int类型随机数
      return getNextIntInRange(range);
    }
    // 处理random.long(1024)类型的随机数
    range = getRange(type, "long");
    if (range != null) {
      // 生成有范围的long类型随机数
      return getNextLongInRange(range);
    }
    // 处理random.uuid类型的随机数
    if (type.equals("uuid")) {
      // 生成随机的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);
  }
}

当我们使用${random.xxx}这种方式获取随机数时,无论是配置文件方式还是@Value方式都会通过org.springframework.boot.env.RandomValuePropertySource#getProperty方法来获取对应类型的随机数。

注意事项:RandomValuePropertySource在继承PropertySource时泛型类型为Randomjava.util.Random类内包含了全部的随机生成逻辑,该类由java提供,有兴趣可以研究下源码。

总结

SpringBoot内的配置都是通过ConfigurablePropertyResolver属性配置解析器来获取的,而该类的实例化在AbstractEnvironment内,我们通过AbstractEnvironment#getProperty(java.lang.String)方法可以获取由多个PropertySource实现类提供的属性配置。

作者个人 博客
使用开源框架 ApiBoot 助你成为Api接口服务架构师

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267