简单高效的随机对象生成器
遇到的问题
在日常开发过程中我们往往有这样的需求:生成随机对象,特别在编写单元测试或集成测试的时候。这样的对象,我们往往不关心其字段的具体是什么值,只要里面的字段符合一定的规则要求即可(避免空指针、满足数据库表非空约束、参数校验约束等)。
java.utiil.random 包给我们提供了 nextInt(), nextLong(), nextDouble(), nextFloat(), nextBytes(), nextBoolean() 和 nextGaussian()
这样的函数,用来生成原始类型对象,但是对于其他对象、字符串等,则没有提供方便的 API。
例如,我们有以下这个 User 对象
@Data
public class User {
@NotBlank
private String username;
@Valid
private Address address;
@Pattern("^1(3|4|5|6|7|8|9)d{9}$");
private String phone;
@Email
private String email;
}
@Data
public class Address {
private String province;
private String city;
private String street;
}
这时我们不得不手动设置每个字段的值
User user = new User();
user.setUsername("张三"):
Address address = new Address();
address.setProvince("浙江");
...
user.setAddress(address);
user.setPhone("13811110000");
user.setEmail("dadiyang@aliyun.com");
当这样的冗余代码充斥在单元测试中时,我们的单元测试会越来越难以阅读和维护,最终导致很难坚持下去,变成烂尾测试。
神器来求场——EasyRandom
EasyRandom,一个简单到*都会用的 Java 对象生成器
The simple, stupid random Java™ beans generator
github地址:https://github.com/j-easy/easy-random
用它来生成随机对象,一行代码搞定:
User user = new EasyRandom().nextObject(User.class);
对于一般的场景,我们直接使用即可。而对于一些个性化的需求,这个生成器还提供了很多个性化的配置选项。
如何使用?
引入maven依赖
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-random-core</artifactId>
<version>4.3.0</version>
<exclusions>
<!-- 跟 SpringAOP 引入的 objenesis 有冲突,要排除 -->
<exclusion>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 支持根据参数校验逻辑生成对象字段 -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-random-bean-validation</artifactId>
<version>4.3.0</version>
</dependency>
注:4.3.0 之前
- 对 @Positive @Nagetive 这类数字类型的支持有bug
- 对于泛型的支持较弱,所以请使用 4.3.0 及以上版本
封装工具类
import org.jeasy.Random.EasyRandom;
import org.jeasy.Random.EasyRandomParameters;
import org.jeasy.Random.api.Randomizer;
import org.jeasy.Random.api.RandomizerRegistry;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.List;
import java.util.Set;
import java.util.stream.*;
/**
* 随机工具,封装 EasyRandom 提供的对象和 List 的随机生成方法
* @author dadiyang
* @since 2020/11/15
*/
public class RandomUtil {
private static final EasyRandom easyRandom
private RandomUtil() {
throw new UnsupportedOperationException("静态工具类不允许被实例化");
}
static {
EasyRandomParameters param = new EasyRandomParameters();
param.setStringLengthRange(new EasyRandomParameters.Range<>(5, 10));
// 注册自定义随机生成器
param.RandomizerRegistry(new BigDecimalRegistry());
// 生成的对象是一个是接口或者抽象类,则扫描类路径找到它的一个具体实现类
param.setScanClasspathForConcreteTypes(true);
param.setObjectPoolSize(100);
param.setRandomizationDepth(12);
easyRandom = new EasyRandom(param);
}
/**
* 根据给定的类型生成一个随机的对象
*/
public static <T> T nextObject(Class<T> clz) {
return easyRandom.nextObject(clz);
}
/**
* 根据给定的类型和大小生成一个随机对象的列表
*/
public static <T> List<T> nextList(Class<T> clz, int size) {
return objects(clz, size).collect(Collectors.toList());
}
/**
* 根据给定的类型和大小生成一个随机对象的集合
*/
public static <T> Set<T> nextSet(Class<T> clz, int size) {
return objects(clz, size).collect(Collectors.toSet());
}
/**
* 根据给定的类型和大小生成一个随机对象流
*/
public static <T> Stream<T> objects(Class<T> clz, int size) {
return easyRandom.objects(clz, size);
}
/**
* 默认生成的 BigDecimal 实例精度过大,将会导致用于插入数据库时超过精度而报错,所以我们默认精度取为5
*/
private static class BigDecimalRegistry implements RandomizerRegistry {
static final BigDecimalRandomizer bigDecimalRandomizer = new BigDecimalRandomizer();
@Override
public void init(EasyRandomParameters easyRandomParameters) { }
@Override
public Randomizer<?> getRandomizer(Field field) {
if (field.getType() == BigDecimal.class) {
return bigDecimalRandomizer;
} else {
return null;
}
}
@Override
public Randomizer<?> getRandomizer(Class<?> aClass) {
if (aClass == BigDecimal.class) {
return bigDecimalRandomizer;
} else {
return null;
}
}
}
private static class BigDecimalRandomizer implements Randomizer<BigDecimal> {
@Override
public BigDecimal getRandomValue() {
return BigDecimal.valueOf(nextDouble(Integer.MIN_VALUE, Integer.MAX_VALUE))
.setScale(5, BigDecimal.ROUND_HALF_UP);
}
}
}
使用工具类
// 随机生成一个 user 对象
User user = RandomUtil.nextObjet(User.class);
// 随机生成一个 user 对象列表
List<User> users = RandomUtil.nextList(User.class, 10);
One More Thing
不用构造器就能生成对象
学习这个工具时,我发现一个很有意思的点。
这个工具使用反射来生成对象并设置里面的值,所以一般我们认为,一个对象应包含一个默认的构造器,这样框架才能正确地使用 clz.newInstance()
来生成一个对象。但是它的强大之处在于,就算是没有默认构造器的对象,它也能够正确地生成对象!
换句话说,在没有默认构造器的场景下,它可以在不需要构造器的情况下,生成一个对象! 是不是非常神奇?原来,它底层是使用了 Objenesis 提供的能力来实现的。这个 Objenesis 在 SpringAOP 的底层也发挥了很大的作用,有兴趣的同学可以继续深入了解一下哦。
推荐阅读
-
60岁拿2000元创业,靠 "神 "做生意,如今年赚24亿--如今的天堂伞集团,无论是口碑还是销量,都是业内首屈一指的 "大牛"。王斌章的一把伞,仅去年的销售额就实现了近6亿元的好成绩。雨伞销售更是占据了中国所有雨伞行业的最大份额。可以说,如今的天堂伞,无论是质量还是口碑,都堪称行业翘楚。 一把天堂伞,如何在王斌章手中打出新高度,玩出大生意?总结起来,两点制胜法宝:一是质量绝对保证,二是服务有保障。这看似很简单,但几十年来不折不扣地执行,特别是在保护伞这个不太重要的对象上,想要做到始终如一,难度很大。这也是为什么中国只有一个王斌章被称为 "全球伞王 "的原因。 天堂伞成立之初,销售场地选在附近的一个广场,以摆摊的形式销售王斌章亲手制作的杭州伞。凭借几十年的手艺和严谨的态度,加上上乘的伞料,即使价格比普通伞高3倍5倍,也打开了市场,积累了第一批人气和资金。
-
如何使用 PHP 开发简单的账户密码生成器
-
掌握 JavaScript 面向对象编程的核心代码:深入分析 JavaScript 面向对象机制的对象基础、原型模式和继承策略全面指导高效创建高质量、可维护的代码 - V. 继承机制
-
简单高效的输入、屏幕截图和笔画翻译软件
-
像首席技术官一样思考:如何高效管理 30 人的研发团队?-管理越多越轻松。好的研发团队,应该是上拨下用,即下级对上级的向上管理;而不是反过来,总是向下管理,甚至是 CTO 做经理的事,经理做工程师的事,工程师最终会被当成实习生。如果是这样,就会越管越累,不仅团队无法成长,而且团队整天很忙还效率低下,问题一大堆。 有这样一个小故事:一位高级经理下班后帮忙倒垃圾,结果被老板训斥了一顿。这就好比首席技术官做了实习生自己该做的事。事情本身没有对错之分,只是从不同的角度有不同的理解。 古人云:"用人不疑,疑人不用"。在面对自己的研发团队时,应该相信他们能做好,授权一线开发人员充分发挥专业特长,不要限制他们的工作。但在相信他们的同时,也要进行二次确认,始终秉持 "我相信,但我要确认 "的原则和严谨的精神。因为每个人都会犯错和疏忽,通过发挥团队的智慧,团队犯错的机会就会大大减少。比如回归测试、代码审查、开发演示、变更审批等等。 如前所述,每个人都难免会犯错。但作为管理者,你所设计和商定的流程不能出错。管理者的每一个决定和沟通都应该经过深思熟虑。就像红绿灯的交通设计,某辆车不小心闯红灯可能会扣分,但红绿灯的设计一定要正确、人性化、统一。再比如,开发人员可能会因为疏忽大意写出 bug,但研发流程的设计和上线流程的发布不能有任何差错。因此,流程体系的设计,一方面要结合当前团队规模、业务特点和需要重点解决的问题来设计,另一方面也要在人员防错、效率提升、发挥团队集体智慧等维度进行综合考量。应该站在更高更抽象的角度去思考,不断思考一个倍受欢迎的园区应该如何设计,思考一个灵动、经典、永恒的建筑应该遵循怎样的模式,思考一个成功、优秀、卓越的研发团队应该需要怎样的流程和制度。 最后,反馈很重要。向上汇报很重要,向下反馈也很重要。能够保持顺畅的双向反馈和闭环管理,对研发团队的协作和沟通有着非常明显的积极作用。在向上汇报方面,要培养团队在正式汇报、会议汇报、私下沟通、书面总结、非正式场合等方面的沟通能力,提醒下属报喜也要报忧。凡事先记录,再跟进,最后反馈。反馈很重要,主动汇报更难得。 另一方面,同时也不要忽视向下反馈。好的爱,是双向的。团队也是如此,没有严格的上下级之分,只是分工和角色不同而已。作为管理者,不必总保持一种 "神秘感",让人 "捉摸不透 "才是牛。当团队做得好或有人做得好时,要记得在公开或私下场合给予肯定和赞许。业务有增长、业绩有提升时,别忘了给团队一些鼓励,或者安排一次下午茶或聚餐。在例会或正式会议上,也可以同步向大家传达一些重要信息和高层指示。"欲速则不达,欲远则同行"。 当向上汇报、向下反馈的沟通闭环形成后,同时结合前面研发过程的管理闭环,双管齐下,就能形成良性循环。如此反复,持之以恒,优秀卓越的研发团队,必将呈现。 能力、产出和效率 接下来,继续重复关于能力、产出和效率的话题。 站在不同的角色,以及一个企业经营、生存和发展所需要的基础上,我把研发生产力分为三个层次,分别是:一线员工关心的研发能力、管理层关心的软件产出和操作人员关心的企业生产效率。简单概括就是:既要把工作做好,又要能出成果,还要能帮企业赚钱。
-
简单的名称生成器
-
好的数据可视化应该简单、易懂、高效、美观。
-
简单高效的 GO 发票识别 + 发票检查界面
-
探索数据之美:数据易--简单高效的自助式数据分析工具
-
简单高效的随机对象生成器