0
点赞
收藏
分享

微信扫一扫

基于JMH的BeanUtils,cglib BeanCopier进行属性拷贝的性能对比

全栈学习笔记 2022-08-18 阅读 93

结论

手动设置属性与cglib性能接近;

cglib/手动设置cglib = 10 * ModelMapper = 100 *Apache BeanUtils

所以不到万不得已不要使用Apache的Bean Utils

工具类

package com.www.common.util;

import org.modelmapper.*;
import org.modelmapper.convention.MatchingStrategies;
import org.modelmapper.spi.DestinationSetter;
import org.modelmapper.spi.MappingContext;
import org.modelmapper.spi.SourceGetter;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.util.CollectionUtils;

import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

/***
* 效率高于Apache BeanUtils, Spring BeanUtils的工具类
*/
public final class PropertyUtils {

private static final ModelMapper modelMapper = new ModelMapper();

private static final Set<PropertyMap> mapperCache = new CopyOnWriteArraySet<PropertyMap>();

private PropertyUtils() {
}

static {
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.getConfiguration().setPropertyCondition(Conditions.isNotNull());
//for skip a field purpose
modelMapper.getConfiguration().setAmbiguityIgnored(true);
}

/***
* 设置属性
* @param trg
* @param propertyName 属性名
* @param value 属性值
*/
public static void setProperty(final Object trg, final String propertyName, final Object value) {
BeanWrapper trgWrap = PropertyAccessorFactory.forBeanPropertyAccess(trg);
//to avoid NullValueInNestedPathException
trgWrap.setAutoGrowNestedPaths(true);
trgWrap.setPropertyValue(propertyName,value);
}

/***
* 将对象<code>src</code>上面的<code>props</code>属性拷贝到对象<code>trg</code>上面
* @param src
* @param trg
* @param props 待拷贝的字段名称
*/
public static void copyProperties(Object src, Object trg, Iterable<String> props) {
BeanWrapper srcWrap = PropertyAccessorFactory.forBeanPropertyAccess(src);
BeanWrapper trgWrap = PropertyAccessorFactory.forBeanPropertyAccess(trg);
//to avoid NullValueInNestedPathException
trgWrap.setAutoGrowNestedPaths(true);
props.forEach(p -> trgWrap.setPropertyValue(p, srcWrap.getPropertyValue(p)));
}

public static void copyProperties(Object src, Object trg, Iterable<String> props, Map<String, String> fieldMapping) {

BeanWrapper srcWrap = PropertyAccessorFactory.forBeanPropertyAccess(src);
BeanWrapper trgWrap = PropertyAccessorFactory.forBeanPropertyAccess(trg);
//to avoid NullValueInNestedPathException
trgWrap.setAutoGrowNestedPaths(true);

if (CollectionUtils.isEmpty(fieldMapping)) {
return;
}

props.forEach(fromFieldName -> {
String toFieldName = fieldMapping.get(fromFieldName);
if (Objects.nonNull(toFieldName)) {
trgWrap.setPropertyValue(toFieldName, srcWrap.getPropertyValue(fromFieldName));
} else {
trgWrap.setPropertyValue(fromFieldName, srcWrap.getPropertyValue(fromFieldName));
}
});
}

public static void copyProperties(Map<?, ?> src,Object trg) {

BeanWrapper trgWrap = PropertyAccessorFactory.forBeanPropertyAccess(trg);
//to avoid NullValueInNestedP athException
trgWrap.setAutoGrowNestedPaths(true);
trgWrap.setPropertyValues(src);
}

/***
* 从map实例上面讲属性拷贝到<code>trg</code>对象属性上面
* @param trg
* @param src
* @param ignoreUnknownProperties 如果<code>trg</code>目标对象不存在map中某个属性,不报错,正常拷贝
*/
public static void copyProperties( Map<?, ?> src, Object trg,boolean ignoreUnknownProperties) {
BeanWrapper trgWrap = PropertyAccessorFactory.forBeanPropertyAccess(trg);
//to avoid NullValueInNestedP athException
trgWrap.setAutoGrowNestedPaths(true);
trgWrap.setPropertyValues(new MutablePropertyValues(src), ignoreUnknownProperties);
}

/***
* 将<code>src</code>上面的属性拷贝到<code>targetClass</code>类型的类实例上
* @param src
* @param targetClass
* @param <T>
* @return
*/
public static <T, S> T mappingProperties(S src, Class<T> targetClass) {
return modelMapper.map(src, targetClass);
}

public static <T,S> T mappingProperties(S src, Class<T> targetClass, Iterable<String> excludeProperties)
{
final TypeMap<?, T> builder = modelMapper.typeMap(src.getClass(), targetClass);
//for(final Iterator<DestinationSetter<T, S>> it = destinationSetters.iterator();it.hasNext();) {
//builder.addMappings(mp -> mp.skip(it.next()));
for(final Iterator<String> it = excludeProperties.iterator(); it.hasNext();){
final String property = it.next();
builder.addMappings(mp->mp.when(new Condition<Object, Object>() {
@Override
public boolean applies(final MappingContext<Object, Object> context) {
if(property.equals(context.getDestination()))
{
return true;
}
return false;
}
}));
}
return null;
}

//TODO:https://stackoverflow.com/questions/49074784/modelmapper-skip-a-field
//TODO:https://www.baeldung.com/java-modelmapper
public static <T, S> T mappingProperties(S src, Class<T> targetClass, Iterable<DestinationSetter<T, S>> destinationSetters,String i) {

final TypeMap<?, T> builder = modelMapper.typeMap(src.getClass(), targetClass);
for(final Iterator<DestinationSetter<T, S>> it = destinationSetters.iterator();it.hasNext();) {
builder.addMappings(mp -> mp.skip(it.next()));
}
PropertyMap<S, T> skipModifiedFieldsMap = new PropertyMap<S, T>() {
@Override
protected void configure() {
// source("d");
// skip().setModifiedBy(null);
// skip().setModifiedDate(null);
}
};
//modelMapper.addMappings(skipModifiedFieldsMap).map(src,targetClass);
return modelMapper.map(src, targetClass);
}

public static <T, S, V> T mappingProperties(S src, Class<T> targetClass,DestinationSetter<T,V> setter) {
final TypeMap<?, T> builder = modelMapper.typeMap(src.getClass(), targetClass);

final TypeMap<?, T> typeMapper = modelMapper.typeMap(src.getClass(), targetClass);
//? super T, ? extends R
SourceGetter<? extends T> getter = null;
//typeMapper.<V>addMapping(getter,setter);

PropertyMap<S, T> skipModifiedFieldsMap = new PropertyMap<S, T>() {
@Override
protected void configure() {
// source("d");
// skip().setModifiedBy(null);
// skip().setModifiedDate(null);
}
};
//modelMapper.addMappings(skipModifiedFieldsMap).map(src,targetClass);
return modelMapper.map(src, targetClass);
}
/***
* 忽略指定字段,PropertyMap用法<pre>
* PropertyMap<ItemDto, Item> skipModifiedFieldsMap = new PropertyMap<ItemDto, Item>() {
* protected void configure() {
* skip().setModifiedBy(null);
* skip().setModifiedDate(null);
* }
* };
* </pre>
* @param src
* @param targetClass
* @param propertyMappingRules
* @param <T>
* @param <S>
* @return
*/
public static <T, S> T mappingProperties(Object src, Class<T> targetClass, PropertyMap<S, T> propertyMappingRules) {
//如果存在映射则不需要再次添加防止报错
if(!mapperCache.contains(propertyMappingRules)) {
modelMapper.addMappings(propertyMappingRules);
mapperCache.add(propertyMappingRules);
}
return mappingProperties(src, targetClass);
}
}

 

测试

package com.www;

import com.www.common.util.PropertyUtils;
import com.www.entity.UserBasicInfo;
import com.www.model.UserModel;

import org.apache.commons.beanutils.BeanUtils;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.springframework.cglib.beans.BeanCopier;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* @author: passedbylove
* @date: Created by 2022/2/9 11:30
* @version: 1.0.0
*/
@BenchmarkMode(Mode.AverageTime) // 测试方法平均执行时间
@OutputTimeUnit(TimeUnit.MILLISECONDS) // 输出结果的时间粒度为微秒
@State(Scope.Thread)
public class BeanCopyPerformanceBenchmark {

private UserModel model = new UserModel();
private BeanCopier beanCopier = BeanCopier.create(UserModel.class, UserBasicInfo.class,false);
LocalDateTime now = LocalDateTime.now();

@Benchmark
public UserBasicInfo manuallySetter()
{
UserBasicInfo vo = new UserBasicInfo();
vo.setCreatedTime(now);
return vo;
}

@Benchmark
public UserBasicInfo beanUtils() throws InvocationTargetException, IllegalAccessException {
UserBasicInfo vo = new UserBasicInfo();
BeanUtils.copyProperties(this.model,vo);
return vo;
}

@Benchmark
public UserBasicInfo beanCopier()
{
UserBasicInfo vo = new UserBasicInfo();
beanCopier.copy(this.model,vo,null);
return vo;
}

@Benchmark
public UserBasicInfo modelMapper()
{
UserBasicInfo vo = PropertyUtils.mappingProperties(this.model,UserBasicInfo.class);
return vo;
}

@Benchmark
public UserBasicInfo propertyAccessor()
{
Set<String> props = new HashSet<>();
props.add("id");
props.add("accountId");
props.add("userType");
props.add("name");
props.add("hrNumber");
props.add("groupId");
props.add("phone");
props.add("email");
props.add("createdBy");
props.add("createdTime");
props.add("updatedBy");
props.add("updatedTime");

UserBasicInfo vo = new UserBasicInfo();
PropertyUtils.copyProperties(this.model,vo,props);
return vo;
}

public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(BeanCopyPerformanceBenchmark.class.getName()+".*").measurementIterations(5).forks(1).build();
new Runner(options).run();
}
}

Benchmark                                      Mode  Cnt   Score    Error  Units
BeanCopyPerformanceBenchmark.beanCopier avgt 5 ≈ 10⁻⁵ ms/op
BeanCopyPerformanceBenchmark.beanUtils avgt 5 0.008 ± 0.001 ms/op
BeanCopyPerformanceBenchmark.manuallySetter avgt 5 ≈ 10⁻⁵ ms/op
BeanCopyPerformanceBenchmark.modelMapper avgt 5 ≈ 10⁻⁴ ms/op
BeanCopyPerformanceBenchmark.propertyAccessor avgt 5 0.003 ± 0.001 ms/op

 

测试2

package com.www;

import com.www.common.util.PropertyUtils;
import com.www.entity.UserBasicInfo;
import com.www.model.UserModel;
import org.apache.commons.beanutils.BeanUtils;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.springframework.cglib.beans.BeanCopier;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
* @author: passedbylove
* @date: Created by 2022/2/9 11:30
* @version: 1.0.0
*/
@BenchmarkMode(Mode.AverageTime) // 测试方法平均执行时间
@OutputTimeUnit(TimeUnit.MILLISECONDS) // 输出结果的时间粒度为微秒
@State(Scope.Thread)
public class CopyPropertyPerformanceBenchmark {
LocalDateTime now = LocalDateTime.now();

@Benchmark
public void manuallySetter()
{
UserBasicInfo vo = new UserBasicInfo();
vo.setCreatedTime(now);
}

@Benchmark
public void beanUtils() throws InvocationTargetException, IllegalAccessException {
UserBasicInfo vo = new UserBasicInfo();
BeanUtils.setProperty(vo,"createdTime",now);
}

@Benchmark
public UserBasicInfo propertyAccessor()
{
UserBasicInfo vo = new UserBasicInfo();
PropertyUtils.setProperty(vo,"createdTime",now);
return vo;
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(CopyPropertyPerformanceBenchmark.class.getName()+".*").measurementIterations(20).forks(1).build();
new Runner(options).run();
}
}

Benchmark                                          Mode  Cnt   Score    Error  Units
CopyPropertyPerformanceBenchmark.beanUtils avgt 20 ≈ 10⁻³ ms/op
CopyPropertyPerformanceBenchmark.manuallySetter avgt 20 ≈ 10⁻⁵ ms/op
CopyPropertyPerformanceBenchmark.propertyAccessor avgt 20 ≈ 10⁻⁴ ms/op

 

举报

相关推荐

0 条评论