xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-tool/src/main/java/org/springblade/core/tool/beans/BladeBeanCopier.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,405 @@
/*
 *      Copyright (c) 2018-2028, DreamLu All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: DreamLu å¢æ˜¥æ¢¦ (596392912@qq.com)
 */
package org.springblade.core.tool.beans;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.ClassUtil;
import org.springblade.core.tool.utils.ReflectUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Label;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
 * spring cglib é­”改
 *
 * <p>
 *     1. æ”¯æŒé“¾å¼ bean,支持 map
 *     2. ClassLoader è·Ÿ target ä¿æŒä¸€è‡´
 * </p>
 *
 * @author L.cm
 */
public abstract class BladeBeanCopier {
   private static final Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter");
   private static final Type BEAN_COPIER = TypeUtils.parseType(BladeBeanCopier.class.getName());
   private static final Type BEAN_MAP = TypeUtils.parseType(Map.class.getName());
   private static final Signature COPY = new Signature("copy", Type.VOID_TYPE, new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER});
   private static final Signature CONVERT = TypeUtils.parseSignature("Object convert(Object, Class, Object)");
   private static final Signature BEAN_MAP_GET = TypeUtils.parseSignature("Object get(Object)");
   private static final Type CLASS_UTILS = TypeUtils.parseType(ClassUtils.class.getName());
   private static final Signature IS_ASSIGNABLE_VALUE = TypeUtils.parseSignature("boolean isAssignableValue(Class, Object)");
   /**
    * The map to store {@link BladeBeanCopier} of source type and class type for copy.
    */
   private static final ConcurrentMap<BladeBeanCopierKey, BladeBeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();
   public static BladeBeanCopier create(Class source, Class target, boolean useConverter) {
      return BladeBeanCopier.create(source, target, useConverter, false);
   }
   public static BladeBeanCopier create(Class source, Class target, boolean useConverter, boolean nonNull) {
      BladeBeanCopierKey copierKey = new BladeBeanCopierKey(source, target, useConverter, nonNull);
      // åˆ©ç”¨ ConcurrentMap ç¼“å­˜ æé«˜æ€§èƒ½ï¼ŒæŽ¥è¿‘ ç›´æŽ¥ get set
      return BEAN_COPIER_MAP.computeIfAbsent(copierKey, key -> {
         Generator gen = new Generator();
         gen.setSource(key.getSource());
         gen.setTarget(key.getTarget());
         gen.setUseConverter(key.isUseConverter());
         gen.setNonNull(key.isNonNull());
         return gen.create(key);
      });
   }
   /**
    * Bean copy
    *
    * @param from from Bean
    * @param to to Bean
    * @param converter Converter
    */
   abstract public void copy(Object from, Object to, @Nullable Converter converter);
   public static class Generator extends AbstractClassGenerator {
      private static final Source SOURCE = new Source(BladeBeanCopier.class.getName());
      private Class source;
      private Class target;
      private boolean useConverter;
      private boolean nonNull;
      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;
      }
      public void setNonNull(boolean nonNull) {
         this.nonNull = nonNull;
      }
      @Override
      protected ClassLoader getDefaultClassLoader() {
         // L.cm ä¿è¯ å’Œ è¿”回使用同一个 ClassLoader
         return target.getClassLoader();
      }
      @Override
      protected ProtectionDomain getProtectionDomain() {
         return ReflectUtils.getProtectionDomain(source);
      }
      @Override
      public BladeBeanCopier create(Object key) {
         return (BladeBeanCopier) super.create(key);
      }
      @Override
      public void generateClass(ClassVisitor v) {
         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);
         // map å•独处理
         if (Map.class.isAssignableFrom(source)) {
            generateClassFormMap(ce, e, sourceType, targetType);
            return;
         }
         // 2018.12.27 by L.cm æ”¯æŒé“¾å¼ bean
         // æ³¨æ„ï¼šæ­¤å¤„需兼容链式bean ä½¿ç”¨äº† spring çš„æ–¹æ³•,比较耗时
         PropertyDescriptor[] getters = ReflectUtil.getBeanGetters(source);
         PropertyDescriptor[] setters = ReflectUtil.getBeanSetters(target);
         Map<String, PropertyDescriptor> names = new HashMap<>(16);
         for (PropertyDescriptor getter : getters) {
            names.put(getter.getName(), getter);
         }
         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);
         for (PropertyDescriptor setter : setters) {
            String propName = setter.getName();
            CopyProperty targetIgnoreCopy = ReflectUtil.getAnnotation(target, propName, CopyProperty.class);
            // set ä¸Šæœ‰å¿½ç•¥çš„ æ³¨è§£
            if (targetIgnoreCopy != null) {
               if (targetIgnoreCopy.ignore()) {
                  continue;
               }
               // æ³¨è§£ä¸Šçš„别名,如果别名不为空,使用别名
               String aliasTargetPropName = targetIgnoreCopy.value();
               if (StringUtil.isNotBlank(aliasTargetPropName)) {
                  propName = aliasTargetPropName;
               }
            }
            // æ‰¾åˆ°å¯¹åº”çš„ get
            PropertyDescriptor getter = names.get(propName);
            // æ²¡æœ‰ get è·³å‡º
            if (getter == null) {
               continue;
            }
            MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
            Method writeMethod = setter.getWriteMethod();
            MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
            Type returnType = read.getSignature().getReturnType();
            Type setterType = write.getSignature().getArgumentTypes()[0];
            Class<?> getterPropertyType = getter.getPropertyType();
            Class<?> setterPropertyType = setter.getPropertyType();
            // L.cm 2019.01.12 ä¼˜åŒ–逻辑,先判断类型,类型一致直接 set,不同再判断 æ˜¯å¦ ç±»åž‹è½¬æ¢
            // nonNull Label
            Label l0 = e.make_label();
            // åˆ¤æ–­ç±»åž‹æ˜¯å¦ä¸€è‡´ï¼ŒåŒ…括 åŒ…装类型
            if (ClassUtil.isAssignable(setterPropertyType, getterPropertyType)) {
               // 2018.12.27 by L.cm æ”¯æŒé“¾å¼ bean
               e.load_local(targetLocal);
               e.load_local(sourceLocal);
               e.invoke(read);
               boolean getterIsPrimitive = getterPropertyType.isPrimitive();
               boolean setterIsPrimitive = setterPropertyType.isPrimitive();
               if (nonNull) {
                  // éœ€è¦è½æ ˆï¼Œå¼ºåˆ¶è£…ç®±
                  e.box(returnType);
                  Local var = e.make_local();
                  e.store_local(var);
                  e.load_local(var);
                  // nonNull Label
                  e.ifnull(l0);
                  e.load_local(targetLocal);
                  e.load_local(var);
                  // éœ€è¦è½æ ˆï¼Œå¼ºåˆ¶æ‹†ç®±
                  e.unbox_or_zero(setterType);
               } else {
                  // å¦‚æžœ get ä¸ºåŽŸå§‹ç±»åž‹ï¼Œéœ€è¦è£…ç®±
                  if (getterIsPrimitive && !setterIsPrimitive) {
                     e.box(returnType);
                  }
                  // å¦‚æžœ set ä¸ºåŽŸå§‹ç±»åž‹ï¼Œéœ€è¦æ‹†ç®±
                  if (!getterIsPrimitive && setterIsPrimitive) {
                     e.unbox_or_zero(setterType);
                  }
               }
               // æž„造 set æ–¹æ³•
               invokeWrite(e, write, writeMethod, nonNull, l0);
            } else if (useConverter) {
               e.load_local(targetLocal);
               e.load_arg(2);
               e.load_local(sourceLocal);
               e.invoke(read);
               e.box(returnType);
               if (nonNull) {
                  Local var = e.make_local();
                  e.store_local(var);
                  e.load_local(var);
                  e.ifnull(l0);
                  e.load_local(targetLocal);
                  e.load_arg(2);
                  e.load_local(var);
               }
               EmitUtils.load_class(e, setterType);
               // æ›´æ”¹æˆäº†å±žæ€§åï¼Œä¹‹å‰æ˜¯ set æ–¹æ³•名
               e.push(propName);
               e.invoke_interface(CONVERTER, CONVERT);
               e.unbox_or_zero(setterType);
               // æž„造 set æ–¹æ³•
               invokeWrite(e, write, writeMethod, nonNull, l0);
            }
         }
         e.return_value();
         e.end_method();
         ce.end_class();
      }
      private static void invokeWrite(CodeEmitter e, MethodInfo write, Method writeMethod, boolean nonNull, Label l0) {
         // è¿”回值,判断 é“¾å¼ bean
         Class<?> returnType = writeMethod.getReturnType();
         e.invoke(write);
         // é“¾å¼ bean,有返回值需要 pop
         if (!returnType.equals(Void.TYPE)) {
            e.pop();
         }
         if (nonNull) {
            e.visitLabel(l0);
         }
      }
      @Override
      protected Object firstInstance(Class type) {
         return BeanUtil.newInstance(type);
      }
      @Override
      protected Object nextInstance(Object instance) {
         return instance;
      }
      /**
       * å¤„理 map çš„ copy
       * @param ce ClassEmitter
       * @param e CodeEmitter
       * @param sourceType sourceType
       * @param targetType targetType
       */
      public void generateClassFormMap(ClassEmitter ce, CodeEmitter e, Type sourceType, Type targetType) {
         // 2018.12.27 by L.cm æ”¯æŒé“¾å¼ bean
         PropertyDescriptor[] setters = ReflectUtil.getBeanSetters(target);
         // å…¥å£å˜é‡
         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);
         Type mapBox = Type.getType(Object.class);
         for (PropertyDescriptor setter : setters) {
            String propName = setter.getName();
            // set ä¸Šæœ‰å¿½ç•¥çš„ æ³¨è§£
            CopyProperty targetIgnoreCopy = ReflectUtil.getAnnotation(target, propName, CopyProperty.class);
            if (targetIgnoreCopy != null) {
               if (targetIgnoreCopy.ignore()) {
                  continue;
               }
               // æ³¨è§£ä¸Šçš„别名
               String aliasTargetPropName = targetIgnoreCopy.value();
               if (StringUtil.isNotBlank(aliasTargetPropName)) {
                  propName = aliasTargetPropName;
               }
            }
            Method writeMethod = setter.getWriteMethod();
            MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
            Type setterType = write.getSignature().getArgumentTypes()[0];
            e.load_local(targetLocal);
            e.load_local(sourceLocal);
            e.push(propName);
            // æ‰§è¡Œ map get
            e.invoke_interface(BEAN_MAP, BEAN_MAP_GET);
            // box è£…箱,避免 array[] æ•°ç»„问题
            e.box(mapBox);
            // ç”Ÿæˆå˜é‡
            Local var = e.make_local();
            e.store_local(var);
            e.load_local(var);
            // å…ˆåˆ¤æ–­ ä¸ä¸ºnull,然后做类型判断
            Label l0 = e.make_label();
            e.ifnull(l0);
            EmitUtils.load_class(e, setterType);
            e.load_local(var);
            // ClassUtils.isAssignableValue(Integer.class, id)
            e.invoke_static(CLASS_UTILS, IS_ASSIGNABLE_VALUE);
            Label l1 = new Label();
            // è¿”回值,判断 é“¾å¼ bean
            Class<?> returnType = writeMethod.getReturnType();
            if (useConverter) {
               e.if_jump(Opcodes.IFEQ, l1);
               e.load_local(targetLocal);
               e.load_local(var);
               e.unbox_or_zero(setterType);
               e.invoke(write);
               if (!returnType.equals(Void.TYPE)) {
                  e.pop();
               }
               e.goTo(l0);
               e.visitLabel(l1);
               e.load_local(targetLocal);
               e.load_arg(2);
               e.load_local(var);
               EmitUtils.load_class(e, setterType);
               e.push(propName);
               e.invoke_interface(CONVERTER, CONVERT);
               e.unbox_or_zero(setterType);
               e.invoke(write);
            } else {
               e.if_jump(Opcodes.IFEQ, l0);
               e.load_local(targetLocal);
               e.load_local(var);
               e.unbox_or_zero(setterType);
               e.invoke(write);
            }
            // è¿”回值,判断 é“¾å¼ bean
            if (!returnType.equals(Void.TYPE)) {
               e.pop();
            }
            e.visitLabel(l0);
         }
         e.return_value();
         e.end_method();
         ce.end_class();
      }
   }
}