解决加了Lombok的@Accessors(chain=true)后无法使用cglib的beanCopier拷贝复制bean的问题

解决加了Lombok的@Accessors(chain=true)后无法使用cglib的beanCopier拷贝复制bean的问题

查看beancopier的源码,发现无法获取包含返回值不为void的set方法.

所以修改beancopier的源码:

  1. 加入了set方法的获取
  2. 改变了字节码的生成
  3. 删除了converter

改变了字节码的生成,测试后速度与原来的相差无几,比Spring的BeanUtils.copyProperties快,有无bug自测


/**
 * @author : seinonana
 * @className : MyBeanCopier
 * @description :
 * @date: 2021-01-12 16:53
 */
/*
 * Copyright 2003,2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import net.sf.cglib.asm.$ClassVisitor;
import net.sf.cglib.asm.$Type;
import net.sf.cglib.core.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

abstract public class MyBeanCopier {
    private static final String SET_PREFIX = "set";

    private static final Logger logger = LoggerFactory.getLogger(MyBeanCopier.class);
    private static Map<String, MyBeanCopier> beanCopierCacheMap = new ConcurrentHashMap<>();

    private static final BeanCopierKey KEY_FACTORY = (BeanCopierKey) KeyFactory.create(BeanCopierKey.class);
    private static final $Type BEAN_COPIER = TypeUtils.parseType(MyBeanCopier.class.getName());
    private static final Signature COPY = new Signature("copy", $Type.VOID_TYPE, new $Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT});

    interface BeanCopierKey {
        Object newInstance(String source, String target, boolean useConverter);
    }

    public static MyBeanCopier create(Class source, Class target, boolean useConverter) {
        Generator gen = new Generator();
        gen.setSource(source);
        gen.setTarget(target);
        gen.setUseConverter(useConverter);
        return gen.create();
    }

    public static <T, R> T copyProperties(R r, Class<T> clazz) {
        T target = null;
        try {
            target = clazz.newInstance();
        } catch (Exception e) {
            logger.error("克隆异常", e);
        }
        String cacheKey = r.getClass().toString() + target.getClass().toString();
        MyBeanCopier beanCopier;
        // 线程1和线程2,同时过来了
        if (!beanCopierCacheMap.containsKey(cacheKey)) {
            // 两个线程都卡这儿了
            // 但是此时线程1先获取到了锁,线程2就等着
            synchronized (MyBeanCopier.class) {
                // 线程1进来之后,发现这里还是没有那个BeanCopier实例
                // 此时线程2,会发现缓存map中已经有了那个BeanCopier实例了,此时就不会进入if判断内的代码
                if (!beanCopierCacheMap.containsKey(cacheKey)) {
                    // 进入到这里会创建一个BeanCopier实例并且放在缓存map中
                    beanCopier = MyBeanCopier.create(r.getClass(), target.getClass(), false);
                    beanCopierCacheMap.put(cacheKey, beanCopier);
                } else {
                    beanCopier = beanCopierCacheMap.get(cacheKey);
                }
            }
        } else {
            beanCopier = beanCopierCacheMap.get(cacheKey);
        }
        beanCopier.copy(r, target);
        return target;
    }

    abstract public void copy(Object from, Object to);

    public static class Generator extends AbstractClassGenerator {
        private static final Source SOURCE = new Source(MyBeanCopier.class.getName());
        private Class source;
        private Class<?> target;
        private boolean useConverter;

        public Generator() {
            super(SOURCE);
        }

        public void setSource(Class source) {
            if (!Modifier.isPublic(source.getModifiers())) {
                setNamePrefix(source.getName());
            }
            this.source = source;
        }

        public void setTarget(Class target) {
            if (!Modifier.isPublic(target.getModifiers())) {
                setNamePrefix(target.getName());
            }
            this.target = target;
        }

        public void setUseConverter(boolean useConverter) {
            this.useConverter = useConverter;
        }

        @Override
        protected ClassLoader getDefaultClassLoader() {
            return source.getClassLoader();
        }

        public MyBeanCopier create() {
            Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
            return (MyBeanCopier) super.create(key);
        }

        @Override
        public void generateClass($ClassVisitor v) throws IntrospectionException, ClassNotFoundException {
            $Type sourceType = $Type.getType(source);
            $Type targetType = $Type.getType(target);
            ClassEmitter ce = new ClassEmitter(v);
            ce.begin_class(Constants.V1_2,
                    Constants.ACC_PUBLIC,
                    getClassName(),
                    BEAN_COPIER,
                    null,
                    Constants.SOURCE_FILE);
            EmitUtils.null_constructor(ce);
            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
            PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);

            List<PropertyDescriptor> setterList=new ArrayList<>();
            Method[] result = target.getMethods();
            //查找set方法
            for (int i = 0; i < result.length; i++) {
                Method method = result[i];
                if (!method.getDeclaringClass().equals(target) || Modifier.isStatic(method.getModifiers())) {
                    continue;
                }
                String name = method.getName();
                Class<?>[] argTypes = method.getParameterTypes();
                int argCount = argTypes.length;
                if (argCount==1&&name.startsWith(SET_PREFIX)) {
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(Introspector.decapitalize(name.substring(3)),null, method);
                    setterList.add( propertyDescriptor);
                }
            }
            PropertyDescriptor[]  setters=setterList.toArray(new PropertyDescriptor[setterList.size()]);
            Map<String, PropertyDescriptor> names = new HashMap(16);
            for (int i = 0; i < getters.length; i++) {
                names.put(getters[i].getName(), getters[i]);
            }
            //将Object类型强转为要转换的类型
            Local targetLocal = e.make_local();
            Local sourceLocal = e.make_local();
            e.load_arg(1);
            e.checkcast(targetType);
            e.store_local(targetLocal);
            e.load_arg(0);
            e.checkcast(sourceType);
            e.store_local(sourceLocal);
            //生成每个setter和getter方法
            for (int i = 0; i < setters.length; i++) {
                PropertyDescriptor setter = setters[i];
                PropertyDescriptor getter = names.get(setter.getName());
                if (getter != null && setter != null) {
                    Method readMethod = getter.getReadMethod();
                    Method writeMethod = setter.getWriteMethod();
                    //set的写方法没找到,或者set的写方法没有参数
                    if (writeMethod == null || readMethod == null) {
                        continue;
                    }
                    MethodInfo read = ReflectUtils.getMethodInfo(readMethod);
                    MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
                    if (compatible(getter, setter)) {
                        e.load_local(targetLocal);
                        e.load_local(sourceLocal);
                        e.invoke(read);
                        e.invoke(write);
                        if (!writeMethod.getReturnType().equals(void.class)){
                            e.pop();
                        }
                    }
                }
            }
            e.return_value();
            e.end_method();
            ce.end_class();
        }

        private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
            return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
        }

        @Override
        protected Object firstInstance(Class type) {
            return ReflectUtils.newInstance(type);
        }

        @Override
        protected Object nextInstance(Object instance) {
            return instance;
        }
    }
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值