# smart-copier
**Repository Path**: swqxdba/smart-copier
## Basic Information
- **Project Name**: smart-copier
- **Description**: 基于javaassist的bean copier生成器
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2023-11-18
- **Last Updated**: 2024-12-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Smart Copier
SmartCopier是一个用于高效处理Bean拷贝、转换的工具。
使用cglib asm库在运行时生成代码,兼顾运行效率和开发效率。
其性能是BeanUtil.copyProperties的200-1000倍,等同于硬编码。
比cglib的beanCopier提供了额外的设置功能,如默认值,是否用null覆盖,是否忽视null属性等,以及设置特殊的属性对应关系等等。
此外,提供更加灵活的转换器,允许只对部分属性进行转换,且不会对其他属性带来额外开销。
# 实现原理:
会在运行时生成一个实现类,该实现类实现Copier接口:
```java
public interface Copier {
/**
* 强制对发现的所有属性进行拷贝
* @param src
* @param target
*/
void copy(Object src,Object target);
/**
* 当源属性不为null时进行拷贝
* @param src
* @param target
*/
void copyNonNullProperties(Object src,Object target);
/**
* 合并两个对象,只对目标对象中为null的属性会被更新
* @param src
* @param target
*/
void merge(Object src,Object target);
}
```
# 快速使用
## 1 引入依赖:
maven
```
io.github.swqxdba
smart-copier
0.0.9
```
## 2 使用
```java
static SmartCopier smartCopier = new SmartCopier();
public static void main(String[] args) {
PersonDto personDto; //...
Person person; //...
smartCopier.copy(person, personDto);
}
```
# 详细介绍
## Copier
Copier是一个接口,定义了三个方法:
```kotlin
interface Copier {
/**
* 强制对发现的所有属性进行拷贝
* @param src
* @param target
*/
fun copy(src: Any?, target: Any?)
/**
* 当源属性不为null时进行拷贝
* 如果源属性为null 但是经过了转换后的目标属性不为null 也会执行更新。
* 如果源属性为null 但是提供了一个非null的默认值,也可能会执行更新
* @param src
* @param target
*/
fun copyNonNullProperties(src: Any?, target: Any?)
/**
* 合并两个对象,只对目标对象中为null的属性会被更新
* 如果某个属性为primitive的 则不会再被merge更新
* @param src
* @param target
*/
fun merge(src: Any?, target: Any?)
}
```
SmartCopier会在运行时,为要拷贝的类型生成对应的Copier实现类。
可以通过SmartCopier.getCopier获取到内部生成的Copier。
多次调用SmartCopier.getCopier会返回同一个Copier对象。Copier是线程安全的。
```java
import io.github.swqxdba.smartcopier.SmartCopier;
static SmartCopier smartCopier = new SmartCopier();
static Copier copier = smartCopier.getCopier(A.class,B.class);
public static void copy(A a , B b) {
copier.copyNonNullProperties(a,b);
}
```
## 配置类(CopyConfig)
配置类用来在生成Copier实例时,对生成代码的逻辑进行定制。
你可以为SmartCopier设置默认的CopyConfig,也可以在每次getCopier时传入CopyConfig。
```java
void example(){
SmartCopier smartCopier = new SmartCopier();
CopyConfig config = //xxx
smartCopier.setDefaultConfig(config);
smartCopier.getCopier(A.class,B.class);
}
void example(){
SmartCopier smartCopier = new SmartCopier();
CopyConfig config = //xxx
smartCopier.getCopier(A.class,B.class,config);
}
```
请注意,使用不同的config时生成的Copier并不一样
以下三个Copier对象是不同的。
```java
void example(){
SmartCopier smartCopier = new SmartCopier();
CopyConfig config1 = //xxx
CopyConfig config2 = //xxx
Copier copier1 = smartCopier.getCopier(A.class,B.class,config);
Copier copier2 = smartCopier.getCopier(A.class,B.class,config2);
Copier copier3 = smartCopier.getCopier(A.class,B.class);
}
```
## SmartCopier类
SmartCopier类是一个Copier的工厂入口,负责生成Copier。不同的SmartCopier生成的Copier不一样。
通常情况下,一个应用只要一个SmartCopier实例即可。
### 让SmartCopier类输出生成的类
你可以让SmartCopier把运行时生成的Copier实例的字节码输出到指定的路径中。
1 启动debug模式
smartCopier.setDebugMode(true);
2 设置输出的class文件路径
smartCopier.setDebugOutPutDir("./");
3 触发copier生成
smartCopier.getCopier(A.class,B.class);
然后你就能在那个路径下看到生成的.class文件,你可以用idea直接打开它来查看其反编译的java代码。
### SmartCopier类快捷方法
你可以直接通过SmartCopier实例执行常用的拷贝方法,而无需获取内部的Copier实例。
```java
void example(A a,B b){
SmartCopier smartCopier = new SmartCopier();
smartCopier.copy(a,b);
//等同于
Copier copier = smartCopier.getCopier(A.class,B.class);
copier.copy(a,b);
}
List example2(List list){
SmartCopier smartCopier = new SmartCopier();
return smartCopier.copyToList(list,B.class);
}
```
在内部,会通过一个ConcurrentHashMap来获取生成的Copier实例执行拷贝。
## Bean
SmartCopier是Bean属性拷贝和转换的工具,依靠Getter、Setter来寻找类中的属性。
除了普通的set get方法外,还会识别出带有返回值的set方法。
```java
A setName(String name){
this.name=name;
return this;
}
```
## 类型转换
在拷贝/转换的过程中,会遇到不一样类型的同名属性,比如:
```java
class Dto{
List children;
}
class Entity{
List children;
}
```
在一些BeanUtil中,只使用class.isAssignableFrom来判断是否能赋值。 但是这么判断不会判断集合元素的内部类型,会造成堆污染。
SmartCopier会执行递归检测,以保证其中的泛型实参也能够互相兼容。如果不兼容,则需要提供转换器进行转换处理,否则不会对该属性进行转换。
在上面的例子中,需要提供一个类型转换器,提供SubDto和SubEntity的转换逻辑。
你可以通过CopyConfig来配置你的转换器。
```java
CopyConfig config = new CopyConfig();
config.addConverter(yourTypeConverterProvider);
```
### TypeConverterProvider
TypeConverterProvider是一个转换器工厂,用于生成转换器实例。每个转换器负责从一个类型到另一个类型的转换。
而TypeConverterProvider负责生成这些转换器。这么设计的好处是,你可以在转换器对象内部保存一些状态。
一个常见的类型转换器是PackageBasedTypeConverterProvider, 它使用一个SmartCopier来对某个包名下的所有类型进行转换。
你可以把项目所在的包名传进去,让其转换项目中的各种类型。
```java
import io.github.swqxdba.smartcopier.CopyConfig;
import io.github.swqxdba.smartcopier.SmartCopier;
@Bean
public SmartCopier smartCopier() {
SmartCopier smartCopier = new SmartCopier();
CopyConfig copyConfig = new CopyConfig();
copyConfig.addConverter(new PackageBasedTypeConverterProvider("com.company.project"),smartCopier);
smartCopier.setDefaultConfig(copyConfig);
return smartCopier;
}
```
### 拆装箱
SmartCopier默认使用BoxTypeConverterProvider进行自动拆装箱,在拆箱时,如果遇到为null的包装类,会返回默认值。
## 集合与数组
集合和数组的转换不好处理,SmartCopier内置了一个ContainerTypeConverterProvider用于处理集合与数组的相关转换。
你可以在CopyConfig中移除掉这个内置的转换器,来取消对集合类型的自动转换。
集合自动转换指的是类似于以下的几种类型间的互相转换,不包括Map的转换。
```java
class A{
List list;
}
class B{
Set list;
}
class C{
Integer[] list;
}
class D{
int[] list;
}
class D{
Collection list;
}
```
```
注意 当你想把一个List转换到int[],且前面的List中存在null元素时,相应的位置会是元素的默认值。
比如有 List = [1,2,null,4]
转换到int[]后会变成 [1,2,0,4]
```
> SmartCopier指的基础类型默认值,是基础类型数组元素的默认值,比如 (new int[1])[0]