采取CGlib达成Bean备份(BeanCopier)版权声明
原创目录
在做生意时,我们有时为了隔离变化,会DAO向外查询Entity,并由外部提供DTO与世隔绝。近似值90%当时,他们的结构很相似,但我们不喜欢写太多冗长的内容b.setF1(a.getF1())这样的代码,所以我们需要BeanCopier来帮助我们。选择Cglib的BeanCopier进行Bean复制的原因是它的性能更好Spring的BeanUtils,Apache的BeanUtils和PropertyUtils这要好得多,特别是在数据量相对较大的情况下。
BeanCopier基本用法

public class User { private int age; private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}


public class UserDto { private int age; private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}


public class UserWithDiffType { private Integer age; private String name;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

- 属性名称和类型相同。:

@Test public void normalCopyTest() { // create(Class source, Class target, boolean useConverter) final BeanCopier beanCopier = BeanCopier.create(User.class, UserDto.class, false**); User user = new User(); user.setAge(10); user.setName("zhangsan"); UserDto userDto = new UserDto(); beanCopier.copy(user, userDto, null**); Assert.assertEquals(10, userDto.getAge()); Assert.assertEquals("zhangsan", userDto.getName()); }

结论: 相同类型、相同房产名称的房产复印件OK 。
- 属性名称相同,类型不同:

@Test public void normalCopyTest() { // create(Class source, Class target, boolean useConverter) final BeanCopier beanCopier = BeanCopier.create(User.class, UserWithDiffType.class, false**); User user = new User(); user.setAge(10); user.setName("zhangsan"); UserWithDiffType userDto = new UserWithDiffType(); beanCopier.copy(user, userDto, null**); Assert.assertEquals(null, userDto.getAge()); Assert.assertEquals("zhangsan", userDto.getName()); }

结论: 不复制特性名称相同但类型不同的特性。 。
注意:即使源类型是原始类型(int, short和char等),目标类型是它的包装类型。(Integer, Short和Character等),或者反之亦然:两者都是 不会被复制。
总结:
BeanCopier只拷贝 具有相同名称和类型的属性 。
自定义转换器
当源类和目标类的属性类型不同时,不能复制该属性,此时我们可以实现Converter接口自定义转换器
源类和目标类:

public class Account { private int id; private Date createTime; private BigDecimal balance;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}


public class AccountDto { private int id; private String createTime; private String balance;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getBalance() {
return balance;
}
public void setBalance(String balance) {
this.balance = balance;
}
}

- 不使用Converter

@Test public void noConverterTest() { Account po = new Account(); po.setId(1); po.setCreateTime(new Date()); po.setBalance(BigDecimal.valueOf(4000L)); BeanCopier copier = BeanCopier.create(Account.class, AccountDto.class, false); AccountDto dto = new AccountDto(); copier.copy(po, dto, null); // 不同类型,未复制 Assert.assertNull(dto.getBalance()); // 不同类型,未复制 Assert.assertNull(dto.getCreateTime()); }

- 使用Converter
根据目标对象的特性,如果源对象具有相同名称的特性,则调整一次。convert方法:

public class TestCase {
@Test
public void noConverterTest() {
Account po = new Account();
po.setId(1);
po.setCreateTime(new Date());
po.setBalance(BigDecimal.valueOf(4000L));
BeanCopier copier = BeanCopier.create(Account.class, AccountDto.class, true);
AccountDto dto = new AccountDto();
AccountConverter converter = new AccountConverter();
copier.copy(po, dto, converter);
// 不同类型,未复制
Assert.assertEquals("4000", dto.getBalance());
// 不同类型,未复制
Assert.assertEquals("2018-12-13", dto.getCreateTime());
}
}
class AccountConverter implements Converter {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@SuppressWarnings("rawtypes")
@Override
public Object convert(Object source, Class target, Object context) {
if (source instanceof Integer) {
return (Integer) source;
} else if (source instanceof Date) {
Date date = (Date) source;
return sdf.format(date);
} else if (source instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) source;
return bd.toPlainString();
}
return null;
}
}

注:一旦使用Converter,BeanCopier只使用Converter定义的规则复制属性,因此在中。convert方法中应考虑所有属性。
封装BeanCopier

@Test
public void costTest() {
List

经过测试,BeanCopier性能是BeanUtils10倍左右。
BeanCopier拷贝速度很快,创建过程中会出现性能瓶颈BeanCopier在实例的过程中。 所以,把创造的BeanCopier实例放入缓存,下次可直接获取以提升性能:
依赖:


封装工具

public class WrapperBeanCopier {
private WrapperBeanCopier() {
//do nothing
}
private static final Map BEAN\_COPIER\_CACHE = new ConcurrentHashMap<>();
private static final Map CONSTRUCTOR\_ACCESS\_CACHE = new ConcurrentHashMap<>();
public static void copyProperties(Object source, Object target) {
BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
copier.copy(source, target, null);
}
private static BeanCopier getBeanCopier(Class sourceClass, Class targetClass) {
String beanKey = generateKey(sourceClass, targetClass);
BeanCopier copier = null;
if (!BEAN\_COPIER\_CACHE.containsKey(beanKey)) {
copier = BeanCopier.create(sourceClass, targetClass, false);
BEAN\_COPIER\_CACHE.put(beanKey, copier);
} else {
copier = BEAN\_COPIER\_CACHE.get(beanKey);
}
return copier;
}
/**
* 这两个类的完全限定名拼接在一起以形成Key
*
* @param sourceClass
* @param targetClass
* @return
*/
private static String generateKey(Class> sourceClass, Class> targetClass) {
return sourceClass.getName() + targetClass.getName();
}
public static T copyProperties(Object source, Class targetClass) {
T t = null;
try {
t = targetClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(format("Create new instance of %s failed: %s", targetClass, e.getMessage()));
}
copyProperties(source, t);
return t;
}
public static List copyPropertiesOfList(List> sourceList, Class targetClass) {
if (sourceList == null || sourceList.isEmpty()) {
return Collections.emptyList();
}
ConstructorAccess constructorAccess = getConstructorAccess(targetClass);
List resultList = new ArrayList<>(sourceList.size());
for (Object o : sourceList) {
T t = null;
try {
t = constructorAccess.newInstance();
copyProperties(o, t);
resultList.add(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return resultList;
}
private static ConstructorAccess getConstructorAccess(Class targetClass) {
ConstructorAccess constructorAccess = CONSTRUCTOR\_ACCESS\_CACHE.get(targetClass.getName());
if (constructorAccess != null) {
return constructorAccess;
}
try {
constructorAccess = ConstructorAccess.get(targetClass);
constructorAccess.newInstance();
CONSTRUCTOR\_ACCESS\_CACHE.put(targetClass.toString(), constructorAccess);
} catch (Exception e) {
throw new RuntimeException(format("Create new instance of %s failed: %s", targetClass, e.getMessage()));
}
return constructorAccess;
}
}

版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123



