标签:contain ace private slist lis 字符串拼接 framework base 大量
结合阿里代码规范约定+源码剖析属性拷贝安全,性能问题
org.springframework.beans.BeanUtils源码为例
第一个标记处校验了源对象中是否有目标对象中需要更新的某属性,如果有就往下执行了
第二个标记处从原对象中把该属性值取出,然后设置到目标对象中相应属性上
对比了其他几种BeanUtils源码基本都是这个实现思路,大同小异,并不会检查原对象的属性是否为null,我想这可能是因为BeanUtils拷贝属性的设计初衷就是null也是一种属性值的状态
【不是不可以使用,请大家使用前务必多做了解,测试;再投入使用】
**********************************************************************************************************************************************************************************************
测试各copy的性能
自定义接口测试方法
public interface PropertiesCopier { void copyProperties(Object source, Object target) throws Exception; } public class CglibBeanCopierPropertiesCopier implements PropertiesCopier { @Override public void copyProperties(Object source, Object target) throws Exception { BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false); copier.copy(source, target, null); } } // 全局静态 BeanCopier,避免每次都生成新的对象 public class StaticCglibBeanCopierPropertiesCopier implements PropertiesCopier { private static BeanCopier copier = BeanCopier.create(Account.class, Account.class, false); @Override public void copyProperties(Object source, Object target) throws Exception { copier.copy(source, target, null); } } public class SpringBeanUtilsPropertiesCopier implements PropertiesCopier { @Override public void copyProperties(Object source, Object target) throws Exception { org.springframework.beans.BeanUtils.copyProperties(source, target); } } public class CommonsBeanUtilsPropertiesCopier implements PropertiesCopier { @Override public void copyProperties(Object source, Object target) throws Exception { org.apache.commons.beanutils.BeanUtils.copyProperties(target, source); } } public class CommonsPropertyUtilsPropertiesCopier implements PropertiesCopier { @Override public void copyProperties(Object source, Object target) throws Exception { org.apache.commons.beanutils.PropertyUtils.copyProperties(target, source); } }
参数化单元测试代码:
@RunWith(Parameterized.class) public class PropertiesCopierTest { @Parameterized.Parameter(0) public PropertiesCopier propertiesCopier; // 测试次数 private static List<Integer> testTimes = Arrays.asList(100, 1000, 10_000, 100_000, 1_000_000); // 测试结果以 markdown 表格的形式输出 private static StringBuilder resultBuilder = new StringBuilder("|实现|100|1,000|10,000|100,000|1,000,000|\n").append("|----|----|----|----|----|----|\n"); @Parameterized.Parameters public static Collection<Object[]> data() { Collection<Object[]> params = new ArrayList<>(); params.add(new Object[]{new StaticCglibBeanCopierPropertiesCopier()}); params.add(new Object[]{new CglibBeanCopierPropertiesCopier()}); params.add(new Object[]{new SpringBeanUtilsPropertiesCopier()}); params.add(new Object[]{new CommonsPropertyUtilsPropertiesCopier()}); params.add(new Object[]{new CommonsBeanUtilsPropertiesCopier()}); return params; } @Before public void setUp() throws Exception { String name = propertiesCopier.getClass().getSimpleName().replace("PropertiesCopier", ""); resultBuilder.append("|").append(name).append("|"); } @Test public void copyProperties() throws Exception { Account source = new Account(1, "test1", 30D); Account target = new Account(); // 预热一次 propertiesCopier.copyProperties(source, target); for (Integer time : testTimes) { long start = System.nanoTime(); for (int i = 0; i < time; i++) { propertiesCopier.copyProperties(source, target); } resultBuilder.append((System.nanoTime() - start) / 1_000_000D).append("|"); } resultBuilder.append("\n"); } @AfterClass public static void tearDown() throws Exception { System.out.println("测试结果:"); System.out.println(resultBuilder); } }
测试结果汇总:
结果表明,Cglib 的 BeanCopier 的拷贝速度是最快的,即使是百万次的拷贝也只需要 10 毫秒! 相比而言,最差的是 Commons 包的 BeanUtils.copyProperties 方法,100 次拷贝测试与表现最好的 Cglib 相差 400 倍之多。百万次拷贝更是出现了 2800 倍的性能差异!
是什么原因导致它存在这么大差异呢? 我们接着往下看源码
public void copyProperties(final Object dest, final Object orig) throws IllegalAccessException, InvocationTargetException { // 类型检查 if (orig instanceof DynaBean) { ... } else if (orig instanceof Map) { ... } else { final PropertyDescriptor[] origDescriptors = ... for (PropertyDescriptor origDescriptor : origDescriptors) { ... // 这里每个属性都调一次 copyProperty copyProperty(dest, name, value); } } } public void copyProperty(final Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException { ... // 这里又进行一次类型检查 if (target instanceof DynaBean) { ... } ... // 需要将属性转换为目标类型 value = convertForCopy(value, type); ... } // 而这个 convert 方法在日志级别为 debug 的时候有很多的字符串拼接 public <T> T convert(final Class<T> type, Object value) { if (log().isDebugEnabled()) { log().debug("Converting" + (value == null ? "" : " ‘" + toString(sourceType) + "‘") + " value ‘" + value + "‘ to type ‘" + toString(targetType) + "‘"); } ... if (targetType.equals(String.class)) { return targetType.cast(convertToString(value)); } else if (targetType.equals(sourceType)) { if (log().isDebugEnabled()) { log().debug("No conversion required, value is already a " + toString(targetType)); } return targetType.cast(value); } else { // 这个 convertToType 方法里也需要做类型检查 final Object result = convertToType(targetType, value); if (log().isDebugEnabled()) { log().debug("Converted to " + toString(targetType) + " value ‘" + result + "‘"); } return targetType.cast(result); } }
你会发现
common的工具类存在反复对象的比对,检查,以及类型转换;在加之又输出了大量的log 其实这就是直接导致其比较慢的原因
总结两点:1 反复对象比对 + 转型 2 大量log的输出
***************************************************************************************************************************************************************************
其实就算使用cglib的工具类,频繁使用也会引起性能瓶颈问题
下面给大家推荐一套解决方案,1、选择cglib的工具类 2,即使频繁使用也不会导致性能瓶颈
/** * * @ClassName: CacheBeanCopier * <b>Copyright xxxx * @Description: 缓存 Bean Copier提升性能 * @author liuhanlin * @date 2020年5月20日 下午4:01:34 * */ public class CacheBeanCopier { private static final Map<String, BeanCopier> BEAN_COPIERS = new HashMap<String, BeanCopier>(); public static void copy(Object source, Object target){ String key = genKey(source.getClass(), target.getClass()); BeanCopier copier = null; if(!BEAN_COPIERS.containsKey(key)){ copier = BeanCopier.create(source.getClass(), target.getClass(), false); BEAN_COPIERS.put(key, copier); }else{ copier = BEAN_COPIERS.get(key); } copier.copy(source, target, null); } private static String genKey(Class<?> sourceClz, Class<?> targetClz){ return sourceClz.toString() + targetClz.toString(); } public static void main(String[] args) { Param1 p1 = new Param1(); p1.setPrd("TOD"); p1.setCntrctNm("you"); p1.setOptnHdgSpotFwdIndctr("1"); p1.setOptnSpotIndctr("0"); p1.setIntrNlegBaseCcyTrdngDir("S"); p1.setFrLegBaseAmnt(new BigDecimal(1500)); p1.setFrLegTermAmnt(new BigDecimal(899)); p1.setNrLegBaseAmnt(new BigDecimal(100)); p1.setNrLegTermAmnt(new BigDecimal(56)); p1.setIntrFlegBaseCcyDir("B"); p1.setNlegSpotExchngRate(new BigDecimal(2)); p1.setFlegSpotExchngRate(new BigDecimal(0.5)); p1.setNrLegVlDt(new Date()); p1.setFrLegVlDt(new Date()); p1.setNtngAmnt(new BigDecimal(2.3)); p1.setClrngCcy("CNY"); p1.setTcktTp("2"); p1.setTcktId("6.25654313"); Param2 p2 = new Param2(); CacheBeanCopier.copy(p1, p2); System.out.println(new Gson().toJson(p2)); } }
本文最后才想起来,依赖也给大家贴出来:
今天突然想到这个问题就更新了下,有问题欢迎沟通交流 email: shubiao_dba@outlook.com
标签:contain ace private slist lis 字符串拼接 framework base 大量
原文地址:https://www.cnblogs.com/lhl-shubiao/p/copy.html