¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * 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(); |
| | | } |
| | | } |
| | | } |