Skip to content

Instantly share code, notes, and snippets.

@bohrqiu
Last active April 24, 2017 02:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bohrqiu/5046a2a7d983996f0e5a to your computer and use it in GitHub Desktop.
Save bohrqiu/5046a2a7d983996f0e5a to your computer and use it in GitHub Desktop.
对象属性复制工具类,采用javassit生成源代码实现属性复制.并能实现包装类型和基础类型之间的转换
package bohr.javassist;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 对象属性复制工具类,采用javassit生成源代码实现属性复制.并能实现包装类型和基础类型之间的转换
* <h3>Usage Examples</h3>
*
* <pre class="code">
* {@code
* TestBean testBean = new TestBean();
* Copier.copy(TestBean1.createTest(), testBean);
* Copier.copy(TestBean1.createTest(), testBean,"t1");
* }
* </pre>
* @author bohr.qiu@gmail.com
*/
public class Copier {
private static final Logger logger = LoggerFactory.getLogger(Copier.class);
private static final String packageName = getPackageName(Copier.class);
/**
* 生成class dump路径
*/
public static String dumpClass = null;
/**
* 打印生成源代码
*/
public static boolean logSource = false;
/**
* Copy实现类对象缓存
*/
private static final Map<Key, Copy> copierMap = new ConcurrentHashMap<Key, Copy>();
private static interface Copy {
void copy(Object source, Object target);
}
/**
* 属性复制
* @param from 源对象
* @param to 目标对象
* @param ignorePropeties 忽略属性
*/
public static void copy(Object from, Object to, String... ignorePropeties) {
Key key = getKey(notNull(from, "源对象不能为空"), notNull(to, "目标对象不能为空"), ignorePropeties);
Copy copy = copierMap.get(key);
if (copy == null) {
synchronized (Copier.class) {
copy = copierMap.get(key);
if (copy == null) {
Copier.Generator generator = new Copier.Generator();
generator.setSource(from.getClass());
generator.setTarget(to.getClass());
generator.setIgnorePropeties(ignorePropeties);
try {
copy = generator.generate().newInstance();
copierMap.put(key, copy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
copy.copy(from, to);
}
private static Key getKey(Object from, Object to, String[] ignoreProperties) {
Class<?> fromClass = from.getClass();
Class<?> toClass = to.getClass();
return new Key(fromClass, toClass, ignoreProperties);
}
private static String getPackageName(Class<?> clazz) {
String className = clazz.getName();
int lastDotIndex = className.lastIndexOf(".");
return (lastDotIndex != -1 ? className.substring(0, lastDotIndex) : "");
}
private static <T> T notNull(T obj, String message) {
if (obj == null) {
throw new NullPointerException(message);
}
return obj;
}
private static <T> T notNull(T obj) {
return notNull(obj, null);
}
private static class Generator {
private static AtomicInteger classNameIndex = new AtomicInteger(1000);
private final String SOURCE = "s";
private final String TARGET = "t";
private Class source;
private Class target;
private String[] ignorePropeties;
private String beginSource;
private List<String> propSources = new ArrayList<String>();
private String endSources;
public void setSource(Class source) {
this.source = source;
}
public void setTarget(Class target) {
this.target = target;
}
public String[] getIgnorePropeties() {
return ignorePropeties;
}
public void setIgnorePropeties(String[] ignorePropeties) {
this.ignorePropeties = ignorePropeties;
}
private void generateBegin() {
//生成方法签名public void copy(TestBean s, TestBean1 t) {
beginSource = "public void copy(Object " + SOURCE + "1 ,Object " + TARGET + "1){\n";
//强制转换源对象
String convertSource = source.getName() + " " + SOURCE + " =" + "(" + source.getName()
+ ")" + SOURCE + "1;\n";
//强制转换目标对象
String convertTarget = target.getName() + " " + TARGET + " =" + "(" + target.getName()
+ ")" + TARGET + "1;\n";
beginSource += convertSource + convertTarget;
}
private void generateEnd() {
endSources = "\n}";
}
private void generateBody() {
PropertyDescriptor[] getters = getPropertyDescriptors(source);
PropertyDescriptor[] setters = getPropertyDescriptors(target);
Map<String, PropertyDescriptor> getterMap = new HashMap<String, PropertyDescriptor>();
for (PropertyDescriptor getter : getters) {
getterMap.put(getter.getName(), getter);
}
for (PropertyDescriptor setter : setters) {
PropertyDescriptor getter = getterMap.get(setter.getName());
if (!checkCanGenSource(setter, getter)) {
continue;
}
Method readMethod = getter.getReadMethod();
Method writeMethod = setter.getWriteMethod();
String readMethodName = readMethod.getName();
String writerMethodName = writeMethod.getName();
if (compatible(getter, setter)) {
propSources.add(genPropertySource(writerMethodName, SOURCE + "."
+ readMethodName + "()"));
} else {
//是否是包装类转换
if (compatibleWrapper(getter, setter)) {
Convertor convert = new Convertor(setter.getPropertyType(), SOURCE,
readMethod.getName());
String f = convert.convert();
if (f != null) {
if (isWrapClass(getter.getPropertyType())) {
String source = genCheckWrapperIsNotNullSource(readMethod.getName());
source += "\t" + genPropertySource(writerMethodName, f);
propSources.add(source);
} else {
propSources.add(genPropertySource(writerMethodName, f));
}
continue;
}
}
warnCantConvert(setter, getter);
}
}
}
private String genCheckWrapperIsNotNullSource(String readName) {
return "if(" + SOURCE + "." + readName + "()!=null)\n";
}
private String genPropertySource(String writerMethodName, String getterSource) {
return TARGET + "." + writerMethodName + "(" + getterSource + ");\n";
}
private void warnCantConvert(PropertyDescriptor setter, PropertyDescriptor getter) {
logger.warn("[对象属性复制]属性类型转换失败{}.{}({})->{}.{}({})", getter.getReadMethod()
.getDeclaringClass().getSimpleName(), getter.getName(), getter.getPropertyType(),
setter.getWriteMethod().getDeclaringClass().getSimpleName(), setter.getName(),
setter.getPropertyType());
}
/**
* 检查是否可以生成源代码
*/
private boolean checkCanGenSource(PropertyDescriptor setter, PropertyDescriptor getter) {
//是否被忽略
if (ignorePropeties != null && isIgnoredProperty(setter)) {
return false;
}
//检查getter是否存在
if (getter == null) {
logger.warn("[对象属性复制]原对象[{}.{}]getter方法不存在", source.getCanonicalName(),
setter.getName());
return false;
}
//检查getter的读方法是否存在
if (getter.getReadMethod() == null) {
logger.warn("[对象属性复制]原对象[{}.{}]getter方法不存在", source.getCanonicalName(),
getter.getName());
return false;
}
//检查setter的写方法是否存在
if (setter.getWriteMethod() == null) {
logger.warn("[对象属性复制]目标对象[{}.{}]setter方法不存在", target.getCanonicalName(),
setter.getName());
return false;
}
return true;
}
private boolean compatibleWrapper(PropertyDescriptor getter, PropertyDescriptor setter) {
return isWrapClass(getter.getPropertyType(), setter.getPropertyType())
|| isWrapClass(setter.getPropertyType(), getter.getPropertyType());
}
private boolean isIgnoredProperty(PropertyDescriptor descriptor) {
String propertyName = descriptor.getName();
for (String ignorePropety : ignorePropeties) {
if (ignorePropety.equals(propertyName)) {
return true;
}
}
return false;
}
public Class<Copy> generate() {
generateBegin();
generateBody();
generateEnd();
StringBuilder sb = new StringBuilder();
sb.append(beginSource);
for (String propSource : propSources) {
sb.append(propSource);
}
sb.append(endSources);
String source = sb.toString();
if (logSource) {
logger.info("\n{}", source);
}
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass(packageName + ".CopierImpl"
+ classNameIndex.incrementAndGet());
Class<Copy> copyClass = null;
try {
cc.addInterface(pool.get(Copy.class.getName()));
CtMethod m = CtNewMethod.make(source, cc);
cc.addMethod(m);
if (dumpClass != null) {
CtClass.debugDump = dumpClass;
}
copyClass = cc.toClass(getDefaultClassLoader(), null);
} catch (Exception e) {
throw new RuntimeException(e);
}
return copyClass;
}
private boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
}
private ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
//ignore
}
if (cl == null) {
cl = this.getClass().getClassLoader();
}
return cl;
}
public PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) {
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(clazz);
return beanInfo.getPropertyDescriptors();
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
}
/**
* 是否为包装类
*/
public static boolean isWrapClass(Class clazz) {
try {
return ((Class) clazz.getField("TYPE").get(null)).isPrimitive();
} catch (Exception e) {
return false;
}
}
/**
* source对象类型是否是target对象类型的包装类
*/
public static boolean isWrapClass(Class source, Class target) {
if (!target.isPrimitive()) {
return false;
}
try {
return source.getField("TYPE").get(null) == target;
} catch (Exception e) {
return false;
}
}
}
/**
* 把source转换为target需要的包装器类型或者原始类型
*/
private static class Convertor {
private String sourceName;
private String readMethodName;
private Class<?> targetType;
private Convertor(Class<?> targetType, String sourceName, String readMethodName) {
this.targetType = notNull(targetType);
this.sourceName = notNull(sourceName);
this.readMethodName = notNull(readMethodName);
}
public String convert() {
if (targetType.isPrimitive()) {
String f = getPrimitiveFormat();
return getterSource() + "." + f + "()";
} else if (Generator.isWrapClass(targetType)) {
String f = getWrapperFormat();
return f + "(" + getterSource() + ")";
} else {
return null;
}
}
private String getterSource() {
return sourceName + "." + readMethodName + "()";
}
private String getPrimitiveFormat() {
return targetType.getName() + "Value";
}
private String getWrapperFormat() {
return targetType.getSimpleName() + ".valueOf";
}
}
private static class Key {
private Class<?> fromClass;
private Class<?> toClass;
private String[] ignoreProperties;
public Key(Class<?> fromClass, Class<?> toClass) {
this.fromClass = fromClass;
this.toClass = toClass;
}
public Key(Class<?> fromClass, Class<?> toClass, String[] ignoreProperties) {
super();
this.fromClass = fromClass;
this.toClass = toClass;
this.ignoreProperties = ignoreProperties;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Key key = (Key) o;
if (!fromClass.equals(key.fromClass))
return false;
if (!Arrays.equals(ignoreProperties, key.ignoreProperties))
return false;
if (!toClass.equals(key.toClass))
return false;
return true;
}
@Override
public int hashCode() {
int result = fromClass.hashCode();
result = 31 * result + toClass.hashCode();
result = 31 * result
+ (ignoreProperties != null ? Arrays.hashCode(ignoreProperties) : 0);
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment