import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
import static org.springframework.beans.BeanUtils.getPropertyDescriptors;
public class BeanUtils {
/**
* List集合Bean转换
*
* @param sourceList 被转换Bean的集合
* @param clazz 目标对象Class
* @param <T> 目标对象类型
* @return 目标对象集合
*/
public static <T> List<T> convertBeanList(List<?> sourceList, Class<T> clazz) {
return sourceList.stream()
.map(r -> convertBean(r, clazz))
.collect(Collectors.toList());
}
/**
* 单Bean转换
*
* @param source 被转换对象
* @param clazz 目标对象Class
* @param <T> 目标对象类型
* @return 目标对象
*/
public static <T> T convertBean(Object source, Class<T> clazz) {
if (null == source) {
return null;
}
T bean = (T)org.springframework.beans.BeanUtils.instantiateClass(clazz);
copyProperties(source, bean, true);
return bean;
}
/**
* 合并对象中属性到目标对象中
*
* @param target 目标对象
* @param sources 源对象列表
* @throws BeansException
*/
public static void merge(Object target, Object... sources) throws BeansException {
for (Object source : sources) {
copyProperties(source, target, false);
}
}
/**
* 合并对象中属性到目标对象中
*
* @param targetClass 目标对象
* @param sources 源对象列表
* @throws BeansException
*/
public static <T> T merge(Class<T> targetClass, Object... sources) throws BeansException {
T target = (T)org.springframework.beans.BeanUtils.instantiateClass(targetClass);
for (Object source : sources) {
copyProperties(source, target, false);
}
return target;
}
/**
* Copy the property values of the given source bean into the target bean.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
*
* @param source the source bean
* @param target the target bean
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
private static void copyProperties(Object source, Object target, boolean copyNull) throws BeansException {
copyProperties(source, target, copyNull, null, (String[])null);
}
/**
* Copy the property values of the given source bean into the given target bean.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source the source bean
* @param target the target bean
* @param editable the class (or interface) to restrict property setting to
* @param ignoreProperties array of property names to ignore
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
@SuppressWarnings("unchecked")
private static void copyProperties(Object source, Object target, boolean copyNull, Class<?> editable,
String... ignoreProperties)
throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
//当类型可以直接转换时
if (ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (value == null && !copyNull) {
continue;
}
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
} else {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (value == null && !copyNull) {
continue;
}
//扩展的枚举相关逻辑
if (Objects.equals(writeMethod.getParameterTypes()[0], String.class)
&& value instanceof Enum) {
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
Enum<?> enumValue = (Enum<?>)value;
writeMethod.invoke(target, enumValue.name());
} else if (value instanceof String
&& writeMethod.getParameterTypes()[0].isEnum()) {
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
String stringValue = (String)value;
if (StringUtils.isBlank(stringValue)) {
continue;
}
Class<Enum> enumTargetType = (Class<Enum>)writeMethod.getParameterTypes()[0];
writeMethod.invoke(target, convertStringToEnum(enumTargetType, stringValue));
}
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
}
private static Enum convertStringToEnum(Class<Enum> clazz, String s) {
if (Enum.class.isAssignableFrom(clazz)) {
return Enum.valueOf(clazz, s);
}
return null;
}
}