Browse Source

增加更多数据结构的copy

zhou-hao 7 years ago
parent
commit
c8095f5225

+ 4 - 0
hsweb-boost/hsweb-boost-compiler/pom.xml

@@ -26,5 +26,9 @@
             <groupId>org.springframework</groupId>
             <artifactId>spring-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 3 - 1
hsweb-boost/hsweb-boost-compiler/src/main/java/org/hswebframework/web/boost/Compiler.java

@@ -46,10 +46,11 @@ public class Compiler<I> {
             ClassPath classPath = new ClassClassPath(this.getClass());
             classPool.insertClassPath(classPath);
         }
-        className = superClass.getSimpleName() + "HSwebBoostGen" + counter.getAndAdd(1);
+        className = superClass.getSimpleName() + "FastBeanCopier" + counter.getAndAdd(1);
         classFullName = superClass.getPackage() + "." + className;
 
         ctClass = classPool.makeClass(classFullName);
+
         if (superClass != Object.class) {
             if (superClass.isInterface()) {
                 ctClass.setInterfaces(new CtClass[]{classPool.get(superClass.getName())});
@@ -57,6 +58,7 @@ public class Compiler<I> {
                 ctClass.setSuperclass(classPool.get(superClass.getName()));
             }
         }
+//        ctClass.debugWriteFile();
         addConstructor("public " + className + "(){}");
     }
 

+ 113 - 51
hsweb-boost/hsweb-boost-compiler/src/main/java/org/hswebframework/web/boost/bean/FastBeanCopier.java

@@ -3,6 +3,7 @@ package org.hswebframework.web.boost.bean;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.beanutils.BeanUtilsBean;
 import org.apache.commons.beanutils.PropertyUtilsBean;
 import org.hswebframework.web.boost.Compiler;
@@ -26,6 +27,7 @@ import java.util.stream.Stream;
  * @author zhouhao
  * @since 3.0
  */
+@Slf4j
 public class FastBeanCopier {
     private static final Map<CacheKey, Copier> CACHE = new HashMap<>();
 
@@ -63,8 +65,8 @@ public class FastBeanCopier {
     }
 
     public static Copier getCopier(Object source, Object target, boolean autoCreate) {
-        Class sourceType = ClassUtils.getUserClass(source);
-        Class targetType = ClassUtils.getUserClass(target);
+        Class sourceType = source instanceof Map ? Map.class : ClassUtils.getUserClass(source);
+        Class targetType = target instanceof Map ? Map.class : ClassUtils.getUserClass(target);
         CacheKey key = createCacheKey(sourceType, targetType);
         if (autoCreate) {
             return CACHE.computeIfAbsent(key, k -> createCopier(sourceType, targetType));
@@ -80,7 +82,7 @@ public class FastBeanCopier {
             return target;
         }
         getCopier(source, target, true)
-                .copy(source, target, (ignore == null || ignore.length == 0) ? new HashSet<>() : new HashSet<>(Arrays.asList(ignore)), converter);
+                .copy(source, target, (ignore == null || ignore.length == 0) ? Collections.emptySet() : new HashSet<>(Arrays.asList(ignore)), converter);
         return target;
     }
 
@@ -99,9 +101,14 @@ public class FastBeanCopier {
                 "\tthrow new RuntimeException(e.getMessage(),e);" +
                 "\n}\n" +
                 "\n}";
-        return Compiler.create(Copier.class)
-                .addMethod(method)
-                .newInstance();
+        try {
+            return Compiler.create(Copier.class)
+                    .addMethod(method)
+                    .newInstance();
+        } catch (Exception e) {
+            log.error("创建bean copy 代理对象失败:\n{}", method, e);
+            throw new UnsupportedOperationException(e.getMessage(), e);
+        }
     }
 
     private static Map<String, ClassProperty> createProperty(Class type) {
@@ -125,8 +132,9 @@ public class FastBeanCopier {
         //源类型为Map
         if (Map.class.isAssignableFrom(source)) {
             if (!Map.class.isAssignableFrom(target)) {
-                sourceProperties = createProperty(source);
-                targetProperties = createMapProperty(sourceProperties);
+                targetProperties = createProperty(target);
+                sourceProperties = createMapProperty(targetProperties);
+
             }
         } else if (Map.class.isAssignableFrom(target)) {
             if (!Map.class.isAssignableFrom(source)) {
@@ -148,35 +156,25 @@ public class FastBeanCopier {
             if (targetProperty == null) {
                 continue;
             }
-
-            code.append("if(!ignore.contains(\"").append(sourceProperty.getName()).append("\")){\n\t")
-                    .append(targetProperty.generateVar(targetProperty.getName())).append("=").append(sourceProperty.generateGetter(targetProperty.getType()))
-                    .append(";\n");
+            code.append("if(!ignore.contains(\"").append(sourceProperty.getName()).append("\")){\n\t");
             if (!sourceProperty.isPrimitive()) {
+                code.append("if(source.").append(sourceProperty.getReadMethod()).append("!=null){\n");
+            }
+            code.append(targetProperty.generateVar(targetProperty.getName())).append("=").append(sourceProperty.generateGetter(targetProperty.getType()))
+                    .append(";\n");
+
+            if (!targetProperty.isPrimitive()) {
                 code.append("\tif(").append(sourceProperty.getName()).append("!=null){\n");
             }
             code.append("\ttarget.").append(targetProperty.generateSetter(targetProperty.getType(), sourceProperty.getName())).append(";\n");
+            if (!targetProperty.isPrimitive()) {
+                code.append("\t}\n");
+            }
             if (!sourceProperty.isPrimitive()) {
                 code.append("\t}\n");
             }
             code.append("}\n");
         }
-//
-//        Map<String, PropertyDescriptor> sourceCache = Stream.of(propertyUtils.getPropertyDescriptors(source))
-//                .collect(Collectors.toMap(FeatureDescriptor::getName, Function.identity()));
-//        StringBuilder builder = new StringBuilder();
-//
-//        Arrays.asList(propertyUtils.getPropertyDescriptors(target))
-//                .forEach((targetField) -> {
-//                    PropertyDescriptor sourceField = sourceCache.get(targetField.getName());
-//                    if (null != sourceField) {
-//                        builder.append(createFieldCopierCode(sourceField, targetField))
-//                                .append("\n");
-//                    } else {
-//                        //源字段不存在
-//                    }
-//                });
-
         return code.toString();
     }
 
@@ -209,6 +207,10 @@ public class FastBeanCopier {
         }
 
         public String getTypeName() {
+            return getTypeName(type);
+        }
+
+        public String getTypeName(Class type) {
             String targetTypeName = type.getName();
             if (type.isArray()) {
                 targetTypeName = type.getComponentType().getName() + "[]";
@@ -254,24 +256,23 @@ public class FastBeanCopier {
                 String getterCode = "source." + getReadMethod();
 
                 String convert = "converter.convert((Object)(" + (isPrimitive() ? castWrapper(getterCode) : getterCode) + "),"
-                        + targetType.getName() + ".class)";
+                        + getTypeName(targetType) + ".class)";
                 StringBuilder convertCode = new StringBuilder();
 
                 if (targetType != getType()) {
                     if (isPrimitive(targetType)) {
-                        boolean sourceIsWrapper = isWrapper(getType());
+                        boolean sourceIsWrapper = isWrapper();
                         Class targetWrapperClass = wrapperClassMapping.get(targetType);
 
                         Class sourcePrimitive = getPrimitiveType(getType());
                         //目标字段是基本数据类型,源字段是包装器类型
-                        // Integer.valueOf(source.getField()).intValue();
+                        // source.getField().intValue();
                         if (sourceIsWrapper) {
-                            convertCode.append(targetType.getName())
-                                    .append(".valueOf(")
+                            convertCode
                                     .append(getterCode)
                                     .append(".")
                                     .append(sourcePrimitive.getName())
-                                    .append("Value())");
+                                    .append("Value()");
                         } else {
                             //类型不一致,调用convert转换
                             convertCode.append("((").append(targetWrapperClass.getName())
@@ -286,13 +287,10 @@ public class FastBeanCopier {
                         boolean targetIsWrapper = isWrapper(targetType);
                         //源字段类型为基本数据类型,目标字段为包装器类型
                         if (targetIsWrapper) {
-                            Class targetPrimitive = getPrimitiveType(targetType);
                             convertCode.append(targetType.getName())
                                     .append(".valueOf(")
                                     .append(getterCode)
-                                    .append(".")
-                                    .append(targetPrimitive.getName())
-                                    .append("Value())");
+                                    .append(")");
                         } else {
                             convertCode.append("(").append(targetType.getName())
                                     .append(")(")
@@ -300,18 +298,17 @@ public class FastBeanCopier {
                                     .append(")");
                         }
                     } else {
-                        convertCode.append("(").append(targetType.getName())
+                        convertCode.append("(").append(getTypeName(targetType))
                                 .append(")(")
                                 .append(convert)
                                 .append(")");
                     }
                 } else {
-
                     if (Cloneable.class.isAssignableFrom(targetType)) {
                         try {
-                            targetType.getMethod("clone");
-                            convertCode.append("(" + getTypeName() + ")").append(getterCode).append(".clone();");
-                        } catch (NoSuchMethodException e) {
+//                            targetType.getDeclaredMethod("clone");
+                            convertCode.append("(" + getTypeName() + ")").append(getterCode).append(".clone()");
+                        } catch (Exception e) {
                             convertCode.append(getterCode);
                         }
                     } else {
@@ -327,10 +324,7 @@ public class FastBeanCopier {
         }
 
         public BiFunction<Class, String, String> createSetterFunction(Function<String, String> settingNameSupplier) {
-            return (sourceType, paramGetter) -> {
-
-                return settingNameSupplier.apply(paramGetter);
-            };
+            return (sourceType, paramGetter) -> settingNameSupplier.apply(paramGetter);
         }
 
         public String generateGetter(Class targetType) {
@@ -365,13 +359,54 @@ public class FastBeanCopier {
             this.setter = createSetterFunction(paramGetter -> "put(\"" + name + "\"," + paramGetter + ")");
         }
 
+        @Override
+        public String getReadMethod() {
+            return "get(\"" + name + "\")";
+        }
+
         @Override
         public String getReadMethodName() {
             return "get(\"" + name + "\")";
         }
     }
 
+
     static class DefaultConvert implements Converter {
+//        static {
+//            BeanUtilsBean
+//                    .getInstance()
+//                    .getConvertUtils().register(new org.apache.commons.beanutils.Converter() {
+//                @Override
+//                public <T> T convert(Class<T> type, Object value) {
+//                    if (value == null) {
+//                        return null;
+//                    }
+//                    if (value instanceof Collection) {
+//                        return (T) new ArrayList(((Collection) value));
+//                    } else if (value.getClass().isArray()) {
+//                        return (T) new ArrayList(Arrays.asList(((Object[]) value)));
+//                    }
+//                    return null;
+//                }
+//            }, List.class);
+//        }
+
+        public Collection newCollection(Class targetClass) {
+
+            if (targetClass == List.class) {
+                return new ArrayList();
+            } else if (targetClass == Set.class) {
+                return new HashSet();
+            } else if (targetClass == Queue.class) {
+                return new LinkedList();
+            } else {
+                try {
+                    return (Collection) targetClass.newInstance();
+                } catch (Exception e) {
+                    throw new UnsupportedOperationException("不支持的类型:" + targetClass, e);
+                }
+            }
+        }
 
         @Override
         public <T> T convert(Object source, Class<T> targetClass) {
@@ -391,15 +426,42 @@ public class FastBeanCopier {
             if (null != converter) {
                 return converter.convert(targetClass, source);
             }
+            if (Collection.class.isAssignableFrom(targetClass)) {
+                Collection collection = newCollection(targetClass);
+                if (source instanceof Collection) {
+                    collection.addAll(((Collection) source));
+                } else if (source instanceof Object[]) {
+                    collection.addAll(Arrays.asList(((Object[]) source)));
+                } else {
+                    if (source instanceof String) {
+                        String stringValue = ((String) source);
+                        collection.addAll(Arrays.asList(stringValue.split("[,]")));
+                    } else {
+                        collection.add(source);
+                    }
+                }
+                return (T) collection;
+            }
+
+            if (targetClass.isEnum()) {
+                for (T t : targetClass.getEnumConstants()) {
+                    if (((Enum) t).name().equalsIgnoreCase(String.valueOf(source))) {
+                        return t;
+                    }
+                }
+                log.warn("无法将:{}转为枚举:{}", source, targetClass);
+                return null;
+            }
             try {
-                T newTarget = targetClass.newInstance();
+
+                T newTarget = targetClass == Map.class ? (T)new HashMap<>() : targetClass.newInstance();
                 copy(source, newTarget);
                 return newTarget;
             } catch (Exception e) {
-
-                e.printStackTrace();
+                log.warn("复制类型{}->{}失败", source, targetClass, e);
+                throw new UnsupportedOperationException(e.getMessage(), e);
             }
-            return null;
+//            return null;
         }
     }
 

+ 5 - 0
hsweb-boost/hsweb-boost-compiler/src/test/java/org/hswebframework/web/boost/Color.java

@@ -0,0 +1,5 @@
+package org.hswebframework.web.boost;
+
+public enum Color {
+    RED,BLUE
+}

+ 14 - 6
hsweb-boost/hsweb-boost-compiler/src/test/java/org/hswebframework/web/boost/FastBeanCopierTest.java

@@ -1,10 +1,12 @@
 package org.hswebframework.web.boost;
 
 import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.beanutils.BeanUtilsBean;
 import org.hswebframework.web.boost.bean.Converter;
 import org.hswebframework.web.boost.bean.Copier;
 import org.hswebframework.web.boost.bean.FastBeanCopier;
 import org.junit.Test;
+import org.springframework.cglib.beans.BeanCopier;
 
 import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
@@ -27,17 +29,21 @@ public class FastBeanCopierTest {
         source.setAge(100);
         source.setName("测试");
         source.setIds(new String[]{"1", "2", "3"});
+        source.setAge2(2);
+        source.setBoy2(true);
+        source.setNestObject2(Collections.singletonMap("name","mapTest"));
         NestObject nestObject = new NestObject();
         nestObject.setAge(10);
         nestObject.setName("测试2");
         source.setNestObject(nestObject);
-
+        source.setNestObject3(nestObject);
 
         Target target = new Target();
         FastBeanCopier.copy(source, target);
 
         long t = System.currentTimeMillis();
-        for (int i = 100_0000; i > 0; i--) {
+//        Copier copier = FastBeanCopier.getCopier(source, target, true);
+        for (int i = 10000; i > 0; i--) {
             FastBeanCopier.copy(source, target);
         }
         System.out.println(System.currentTimeMillis() - t);
@@ -66,19 +72,21 @@ public class FastBeanCopierTest {
 
 
     @Test
-    public void testCopyToMap() {
+    public void testCopyMap() {
         Source source = new Source();
         source.setAge(100);
         source.setName("测试");
-        source.setIds(new String[]{"1", "2", "3"});
+//        source.setIds(new String[]{"1", "2", "3"});
         NestObject nestObject = new NestObject();
         nestObject.setAge(10);
         nestObject.setName("测试2");
-        source.setNestObject(nestObject);
-        Map<String,Object> target = new HashMap<>();
+//        source.setNestObject(nestObject);
+        Map<String, Object> target = new HashMap<>();
+
         FastBeanCopier.copy(source, target);
 
         System.out.println(target);
+        System.out.println(FastBeanCopier.copy(target, new Source()));
     }
 
 

+ 36 - 1
hsweb-boost/hsweb-boost-compiler/src/test/java/org/hswebframework/web/boost/Source.java

@@ -2,14 +2,49 @@ package org.hswebframework.web.boost;
 
 import lombok.Data;
 
+import java.util.*;
+
 @Data
 public class Source {
     private String name;
-    private int    age;
+    private int age;
     private String[] ids;
 
+    private boolean boy;
+
+    private Integer age2;
+
+    private Boolean boy2;
+
+    private int age3;
+
+    private boolean boy3;
+
     private NestObject nestObject;
 
+    private Map<String,Object> nestObject2=new HashMap<>();
+
+    private NestObject nestObject3;
+
+
+    private Color color = Color.RED;
+
+    private String color2 = Color.RED.name();
+
+    private List<String> arr = Arrays.asList("2","3");
+
+    private List<String> arr4 = Arrays.asList("2","3");
+
+    private String[] arr2 = {"1", "2"};
+
+    private String[] arr3 = {"1", "2"};
+
+    private String[] arr5 = {"1", "2"};
+
+    private String[] arr6 = {"1", "2"};
+
+
+
 }
 
 

+ 32 - 0
hsweb-boost/hsweb-boost-compiler/src/test/java/org/hswebframework/web/boost/Target.java

@@ -2,6 +2,10 @@ package org.hswebframework.web.boost;
 
 import lombok.Data;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 @Data
 public class Target {
     private String name;
@@ -9,4 +13,32 @@ public class Target {
     private String[] ids;
     private NestObject nestObject;
 
+    private NestObject nestObject2;
+
+    private Map<String,Object> nestObject3;
+
+    private int age2;
+
+    private boolean boy2;
+
+    private Boolean boy;
+
+    private String age3;
+
+    private String boy3;
+
+    private String color;
+
+    private Color color2;
+
+    private List<String> arr2;
+
+    private String[] arr ;
+
+    private Integer[] arr3;
+
+    private Integer[] arr4;
+
+
+
 }

+ 0 - 32
hsweb-boost/hsweb-boost-compiler/src/test/java/org/hswebframework/web/boost/Test2.java

@@ -1,32 +0,0 @@
-package org.hswebframework.web.boost;
-
-import org.hswebframework.web.boost.bean.Converter;
-import org.hswebframework.web.boost.bean.Copier;
-
-import java.util.Set;
-
-/**
- * TODO 完成注释
- *
- * @author zhouhao
- * @since
- */
-public class Test2 implements Copier{
-    public void copy(Object s, Object t, java.util.Set ignore, org.hswebframework.web.boost.bean.Converter converter){
-        org.hswebframework.web.boost.Source source=(org.hswebframework.web.boost.Source)s;
-        org.hswebframework.web.boost.Target target=(org.hswebframework.web.boost.Target)t;
-        if(!ignore.contains("name")&&source.getName()!=null){
-            java.lang.String name=source.getName();
-            target.setName(name);
-        }
-        if(!ignore.contains("nestObject")&&source.getNestObject()!=null){
-            org.hswebframework.web.boost.NestObject nestObject=source.getNestObject();
-            target.setNestObject(nestObject);
-        }
-
-        if(!ignore.contains("age")){
-            int age=source.getAge();
-            target.setAge(age);
-        }
-    }
-}