- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在实际的项目开发中,对象间赋值普遍存在,随着双11、秒杀等电商过程愈加复杂,数据量也在不断攀升,效率问题,浮出水面.
答:想都不用想,直接代码走起来,get、set即可.
问:下图这样?
答:对啊,你怎么能把我的代码放到网上?
问:没,我只是举个例子 。
答:这涉及到商业机密,是很严重的问题 。
问:我发现你挺能扯皮啊,直接回答问题行吗?
问:行了,行了,说说,怎么解决吧.
答:很简单啊,可以通过工具类Beanutils直接赋值啊 。
答:就 Apache 自带的那个啊,贼简单。我手写一个,给你欣赏一下.
问:你这代码报错啊,避免用Apache Beanutils进行属性的copy.
答:没报错,只是严重警告而已, 代码能跑就行,有问题再优化呗 。
答:拿多少钱,干多少活,我又不是XXX,应该是性能问题吧 。
问:具体什么原因导致的呢?
答:3000块钱还得手撕一下 apache copyProperties 的源代码呗?
通过单例模式调用 copyProperties ,但是,每一个方法对应一个 BeanUtilsBean.getInstance() 实例,每一个类实例对应一个实例,这不算一个真正的单例模式.
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}
通过源码可以看到,每一个 copyProperties 都要进行多次类型检查,还要打印日志.
/**
* org.apache.commons.beanutils.BeanUtils.copyProperties方法源码解析
* @author 哪吒编程
* @time 2023-01-07
*/
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
// 类型检查
if (dest == null) {
throw new IllegalArgumentException("No destination bean specified");
} else if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
} else {
// 打印日志
if (this.log.isDebugEnabled()) {
this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
}
int var5;
int var6;
String name;
Object value;
// 类型检查
// DanyBean 提供了可以动态修改实现他的类的属性名称、属性值、属性类型的功能
if (orig instanceof DynaBean) {
// 获取源对象所有属性
DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
DynaProperty[] var4 = origDescriptors;
var5 = origDescriptors.length;
for(var6 = 0; var6 < var5; ++var6) {
DynaProperty origDescriptor = var4[var6];
// 获取源对象属性名
name = origDescriptor.getName();
// 判断源对象是否可读、判断目标对象是否可写
if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
// 获取对应的值
value = ((DynaBean)orig).get(name);
// 每个属性都调用一次copyProperty
this.copyProperty(dest, name, value);
}
}
} else if (orig instanceof Map) {
Map<String, Object> propMap = (Map)orig;
Iterator var13 = propMap.entrySet().iterator();
while(var13.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry)var13.next();
String name = (String)entry.getKey();
if (this.getPropertyUtils().isWriteable(dest, name)) {
this.copyProperty(dest, name, entry.getValue());
}
}
} else {
PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);
PropertyDescriptor[] var14 = origDescriptors;
var5 = origDescriptors.length;
for(var6 = 0; var6 < var5; ++var6) {
PropertyDescriptor origDescriptor = var14[var6];
name = origDescriptor.getName();
if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
try {
value = this.getPropertyUtils().getSimpleProperty(orig, name);
this.copyProperty(dest, name, value);
} catch (NoSuchMethodException var10) {
}
}
}
}
}
}
再通过 jvisualvm.exe 检测一下运行情况,果然, logging.log4j 赫然在列,稳居耗时Top1.
package com.nezha.copy;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.StopWatch;
public class Test {
public static void main(String[] args) {
User user = new User();
user.setUserId("1");
user.setUserName("哪吒编程");
user.setCardId("123");
user.setCreateTime("2023-01-03");
user.setEmail("666666666@qq.com");
user.setOperate("哪吒");
user.setOrgId("46987916");
user.setPassword("123456");
user.setPhone("10086");
user.setRemark("456");
user.setSex(1);
user.setStatus("1");
user.setTel("110");
user.setType("0");
user.setUpdateTime("2023-01-05");
User target = new User();
int sum = 10000000;
apacheBeanUtilsCopyTest(user,target,sum);
commonsPropertyCopyTest(user,target,sum);
cglibBeanCopyTest(user,target,sum);
springBeanCopyTest(user,target,sum);
}
private static void apacheBeanUtilsCopyTest(User source, User target, int sum) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < sum; i++) {
apacheBeanUtilsCopy(source,target);
}
stopWatch.stop();
System.out.println("使用org.apache.commons.beanutils.BeanUtils方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
}
/**
* org.apache.commons.beanutils.BeanUtils方式
*/
private static void apacheBeanUtilsCopy(User source, User target) {
try {
BeanUtils.copyProperties(source, target);
} catch (Exception e) {
}
}
private static void commonsPropertyCopyTest(User source, User target, int sum) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < sum; i++) {
commonsPropertyCopy(source,target);
}
stopWatch.stop();
System.out.println("使用org.apache.commons.beanutils.PropertyUtils方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
}
/**
* org.apache.commons.beanutils.PropertyUtils方式
*/
private static void commonsPropertyCopy(User source, User target) {
try {
PropertyUtils.copyProperties(target, source);
} catch (Exception e) {
}
}
private static void cglibBeanCopyTest(User source, User target, int sum) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < sum; i++) {
cglibBeanCopy(source,target);
}
stopWatch.stop();
System.out.println("使用org.springframework.cglib.beans.BeanCopier方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
}
/**
* org.springframework.cglib.beans.BeanCopier方式
*/
static BeanCopier copier = BeanCopier.create(User.class, User.class, false);
private static void cglibBeanCopy(User source, User target) {
copier.copy(source, target, null);
}
private static void springBeanCopyTest(User source, User target, int sum) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < sum; i++) {
springBeanCopy(source,target);
}
stopWatch.stop();
System.out.println("使用org.springframework.beans.BeanUtils.copyProperties方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
}
/**
* org.springframework.beans.BeanUtils.copyProperties方式
*/
private static void springBeanCopy(User source, User target) {
org.springframework.beans.BeanUtils.copyProperties(source, target);
}
}
方法 | 1000 | 10000 | 100000 | 1000000 |
---|---|---|---|---|
apache BeanUtils | 906毫秒 | 807毫秒 | 1892毫秒 | 11049毫秒 |
apache PropertyUtils | 17毫秒 | 96毫秒 | 648毫秒 | 5896毫秒 |
spring cglib BeanCopier | 0毫秒 | 1毫秒 | 3毫秒 | 10毫秒 |
spring copyProperties | 87毫秒 | 90毫秒 | 123毫秒 | 482毫秒 |
不测不知道,一测吓一跳,差的还真的多.
spring cglib BeanCopier 性能最好, apache BeanUtils 性能最差.
性能走势 --> spring cglib BeanCopier 优于 spring copyProperties 优于 apache PropertyUtils 优于 apache BeanUtils 。
避免用Apache Beanutils进行属性的copy的问题 上面分析完了,下面再看看其它的方法做了哪些优化.
从源码可以清晰的看到,类型检查变成了非空校验,去掉了每一次copy的日志记录,性能肯定更好了.
DanyBean 提供了可以动态修改实现他的类的属性名称、属性值、属性类型的功能.
/**
* org.apache.commons.beanutils.PropertyUtils方式源码解析
* @author 哪吒编程
* @time 2023-01-07
*/
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
// 判断数据源和目标对象不是null
if (dest == null) {
throw new IllegalArgumentException("No destination bean specified");
} else if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
} else {
// 删除了org.apache.commons.beanutils.BeanUtils.copyProperties中最为耗时的log日志记录
int var5;
int var6;
String name;
Object value;
// 类型检查
if (orig instanceof DynaBean) {
// 获取源对象所有属性
DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
DynaProperty[] var4 = origDescriptors;
var5 = origDescriptors.length;
for(var6 = 0; var6 < var5; ++var6) {
DynaProperty origDescriptor = var4[var6];
// 获取源对象属性名
name = origDescriptor.getName();
// 判断源对象是否可读、判断目标对象是否可写
if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
try {
// 获取对应的值
value = ((DynaBean)orig).get(name);
// 相对于org.apache.commons.beanutils.BeanUtils.copyProperties此处有优化
// DanyBean 提供了可以动态修改实现他的类的属性名称、属性值、属性类型的功能
if (dest instanceof DynaBean) {
((DynaBean)dest).set(name, value);
} else {
// 每个属性都调用一次copyProperty
this.setSimpleProperty(dest, name, value);
}
} catch (NoSuchMethodException var12) {
if (this.log.isDebugEnabled()) {
this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var12);
}
}
}
}
} else if (orig instanceof Map) {
Iterator entries = ((Map)orig).entrySet().iterator();
while(true) {
Map.Entry entry;
String name;
do {
if (!entries.hasNext()) {
return;
}
entry = (Map.Entry)entries.next();
name = (String)entry.getKey();
} while(!this.isWriteable(dest, name));
try {
if (dest instanceof DynaBean) {
((DynaBean)dest).set(name, entry.getValue());
} else {
this.setSimpleProperty(dest, name, entry.getValue());
}
} catch (NoSuchMethodException var11) {
if (this.log.isDebugEnabled()) {
this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var11);
}
}
}
} else {
PropertyDescriptor[] origDescriptors = this.getPropertyDescriptors(orig);
PropertyDescriptor[] var16 = origDescriptors;
var5 = origDescriptors.length;
for(var6 = 0; var6 < var5; ++var6) {
PropertyDescriptor origDescriptor = var16[var6];
name = origDescriptor.getName();
if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
try {
value = this.getSimpleProperty(orig, name);
if (dest instanceof DynaBean) {
((DynaBean)dest).set(name, value);
} else {
this.setSimpleProperty(dest, name, value);
}
} catch (NoSuchMethodException var10) {
if (this.log.isDebugEnabled()) {
this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var10);
}
}
}
}
}
}
}
再通过jvisualvm.exe检测一下运行情况,果然, logging.log4j 没有了,其他的基本不变.
if (orig instanceof DynaBean) {
这个类型检查;
/**
* org.springframework.beans.BeanUtils.copyProperties方法源码解析
* @author 哪吒编程
* @time 2023-01-07
*/
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
// 判断数据源和目标对象不是null
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
/**
* 若target设置了泛型,则默认使用泛型
* 若是 editable 是 null,则此处忽略
* 一般情况下editable都默认为null
*/
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
// 获取target中全部的属性描述
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
// 需要忽略的属性
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
// 目标对象存在写入方法、属性不被忽略
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
/**
* 源对象存在读取方法、数据是可复制的
* writeMethod.getParameterTypes()[0]:获取 writeMethod 的第一个入参类型
* readMethod.getReturnType():获取 readMethod 的返回值类型
* 判断返回值类型和入参类型是否存在继承关系,只有是继承关系或相等的情况下,才会进行注入
*/
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
// 放开读取方法的权限
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
// 通过反射获取值
Object value = readMethod.invoke(source);
// 放开写入方法的权限
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 通过反射写入值
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
阿里的友情提示,避免用 Apache Beanutils 进行对象的 copy ,还是很有道理的.
Apache Beanutils 的性能问题出现在类型校验和每一次copy的日志记录; 。
if (orig instanceof DynaBean) {
这个类型检查; 最后此篇关于避免用ApacheBeanutils进行属性的copy。why?让我们一起一探究竟的文章就讲到这里了,如果你想了解更多关于避免用ApacheBeanutils进行属性的copy。why?让我们一起一探究竟的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如何将 jQuery 代码转换为 React JS? 我有一个带有文本“复制”的按钮。当我单击它时,应将其文本更改为“已复制”并复制到剪贴板。复制后,几秒钟后我希望文本返回到“复制”。我相信以下功能将
在任何情况下我都想使用 NumPy 的 np.copy() 而不是 Python 的 copy.copy() 方法?据我所知,两者都创建浅拷贝,但 NumPy 仅限于数组。 NumPy 是否有任何性能
%python -m timeit -s "import copy" "x = (1, 2, 3)" "copy.deepcopy(x)" 100000 loops, best of 3: 10.1
我想制作一个列表的副本(字面意思是一个单独的克隆,与原始列表没有任何共享)。我使用了 copy.copy() 并创建了 2 个单独的列表,但为什么每个副本的元素似乎仍然共享? 这很难解释,请查看以下输
我不明白使用通配符时 COPY 命令的行为。 我在 C:\Source 中有一个文本文件叫 mpt*.asm我想把它复制到 C:\Dest .这是批处理脚本所需要的,我不能确定 mpt*.asm 的确
相关但不等同于:Golang: How to copy Context object without deriving 是否可以推导出 context.WithTimeout来自 context.Ba
您可以实现 Copy 特性来为类型提供复制语义而不是 move 语义。仅当其所有组成元素(产品类型的每个因素,或总和类型的每个变体的每个因素)也都是复制时,才能执行此操作。 这还允许您制作相当大的类型
我有一段代码,我需要确定编码值的类型,但我不知道它是字符串、无符号整数还是字符串的矢量。我想做以下几件事:。然而,来自弯曲板条箱的值不能实现复制,它在调用Decode_Bencode_Object之后
我需要复制一些对象,我读到 copy.copy 模块可以在 Python 上执行此操作。问题是,这些对象有一些属性是长数组。 那么这个方法效率高吗?由于性能在我所做的这项工作中很重要。 有更好的方法吗
我尝试高效地制作 lua 表的副本。我编写了以下运行良好的函数 copyTable()(见下文)。但我想我可以使用函数的“按值传递”机制获得更高效的东西。我做了一些测试来探索这个机制: functio
使用 pry 插件:pry-clipboard 当我输入“copy-history”来复制我历史的最后一行时,它实际上是在复制“copy-history”并粘贴“copy-history”。 我是不是
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 11 个月前关闭。 Improve this
我不了解Kotlin中通过访问器处理字段和复制方法之间的区别。就像这样: 访问者示例: class Person(val name: String, var age: Int
如何从节点复制一些属性。例如。我只想从节点“Extn”复制“Srno”,“RollNo”,“right”。
我有以下两个 XSL 转换,我希望将它们链接到一个 XSL 文件中。 第一次转换: 第二个转换(使用第一个转换的输出作为输入): 我的目标是从 WSDL
我是 Vertica DB 的新手,之前使用过 Mysql。我想在 vertica 表中插入唯一记录,但 vertica 在插入时不支持唯一约束。我通过 COPY 查询在表中插入记录。所以我无法在插入
std::copy 与执行策略参数之间是否存在正式关系?无论是在实践中还是在标准中。 例如,会不会只是这样, namespace std{ template It copy(std::
我用 root 运行了以下命令来备份同一主机上的文件夹:cp -r master 主备 size of master : 76GB size of master-backup : 71GB 知道为什么
我遇到过一段代码,乍一看似乎毫无意义。但我意识到这可能会产生一些我不知道的未知含义,因为 Python 不是我最熟悉的语言。 import copy node = copy.copy(node) 阅读
我正在设计一个基类,我希望它为 copy.copy 定义基本行为。此行为包括在控制台中打印警告,然后复制实例,就好像它没有 __copy__ 一样。属性。 当定义一个空白时Foo类并复制它的一个实例,
我是一名优秀的程序员,十分优秀!