美文网首页
利用java字节码技术写了一个BeanUtils实现对象拷贝

利用java字节码技术写了一个BeanUtils实现对象拷贝

作者: TinyThing | 来源:发表于2021-06-30 17:23 被阅读0次

    代码比较简单,只有150行左右,主要利用javassist技术从而避免了反射调用

    经测试,性能是spring beanUtils的20倍左右

    
    /**
     * 一个工具类,用于对象属性拷贝
     * 基于javassist,动态生成字节码
     * 从而避免了反射调用
     *
     * @author guoxiang
     * @version 1.0.0
     * @since 2021/6/30
     */
    public class Copy {
    
        private static final ConcurrentHashMap<ConverterKey, Converter> CACHE = new ConcurrentHashMap<>(32);
        private static final AtomicInteger ID = new AtomicInteger();
        private static CtClass converterInterface;
        static { init(); }
    
        private static void init() {
            CtClass[] nestedClasses = new CtClass[0];
            try {
                nestedClasses = ClassPool.getDefault().get(Copy.class.getName()).getNestedClasses();
            } catch (NotFoundException e) {
                e.printStackTrace();
            }
            Arrays.stream(nestedClasses)
                    .filter(nestedClass -> nestedClass.getName().equals(Converter.class.getName()))
                    .forEach(nestedClass -> converterInterface = nestedClass);
        }
    
    
        public static void copy(Object from, Object to) {
            Class<?> fromClass = from.getClass();
            Class<?> toClass = to.getClass();
    
            Converter converter = getConverter(fromClass, toClass);
            converter.convert(from, to);
        }
    
        /**
         * 从缓存获取converter
         *
         * @param fromClass 源类
         * @param toClass   目标类
         * @return          转换器
         */
        private static Converter getConverter(Class<?> fromClass, Class<?> toClass) {
            ConverterKey key = new ConverterKey(fromClass, toClass);
            return CACHE.computeIfAbsent(key, Copy::generateConverter);
        }
    
    
        /**
         * 使用javassist生成一个转换器
         *
         * @param key   key
         * @return      converter
         */
        private static Converter generateConverter(ConverterKey key) {
    
            Class<?> fromClass = key.fromClass;
            Class<?> toClass = key.toClass;
    
            ClassPool pool = ClassPool.getDefault();
            CtClass converterClass = pool.makeClass("RuntimeConverter" + ID.getAndIncrement());
    
            try {
                converterClass.addInterface(converterInterface);
    
                CtMethod convertMethod = CtNewMethod.make(generateMethod(fromClass, toClass), converterClass);
                converterClass.addMethod(convertMethod);
    
                Class<?> type = converterClass.toClass(Copy.class.getClassLoader(), Copy.class.getProtectionDomain());
                return (Converter) type.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("- generate converter error", e);
            }
        }
    
        /**
         * 生成转换器方法
         *
         * @param fromClass 原始类
         * @param toClass   目标类
         * @return          方法代码
         */
        private static String generateMethod(Class<?> fromClass, Class<?> toClass) {
            String prefix = "public void convert(Object from, Object to) {\n";
            String castFromCode = fromClass.getName() + " a = (" + fromClass.getName() + ") from;\n";
            String castToCode = toClass.getName() + " b = (" + toClass.getName() + ") to;\n";
            String postfix = "}\n";
    
            Set<String> fromFields = getFields(fromClass);
            Set<String> toFields = getFields(toClass);
    
            fromFields.retainAll(toFields);
    
            StringBuilder code = new StringBuilder();
            for (String field : fromFields) {
                field = field.substring(0, 1).toUpperCase() + field.substring(1);
                code.append("b.set").append(field).append("(a.get").append(field).append("());\n");
            }
    
            return prefix + castFromCode + castToCode + code + postfix;
        }
    
        /**
         * 获取一个类(包含父类)的所有属性
         *
         * @param type type
         * @return  属性list
         */
        private static Set<String> getFields(Class<?> type) {
    
            Field[] fields = type.getDeclaredFields();
            Set<String> fieldSet = Stream.of(fields).map(Field::getName).collect(toSet());
    
            Class<?> parent = type.getSuperclass();
            if (type.equals(Object.class) || parent.equals(Object.class)) {
                return fieldSet;
            }
    
            Set<String> parentFieldSet = getFields(parent);
            fieldSet.addAll(parentFieldSet);
    
            return fieldSet;
        }
    
    
        /**
         * 用于缓存的键
         */
        @EqualsAndHashCode
        @AllArgsConstructor
        private static class ConverterKey {
            Class<?> fromClass;
            Class<?> toClass;
        }
    
        public interface Converter {
    
            /**
             * 将一个对象复制到另一个对象
             * @param from  from
             * @param to    to
             */
            void convert(Object from, Object to);
        }
        
    }
    
    
    

    相关文章

      网友评论

          本文标题:利用java字节码技术写了一个BeanUtils实现对象拷贝

          本文链接:https://www.haomeiwen.com/subject/roirultx.html