Skip to content

运行时生成bean copier代码的高效率工具。 使用cglib asm库在运行时生成代码,兼顾运行效率和开发效率。

Notifications You must be signed in to change notification settings

SWQXDBA/smart-copier

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Smart Copier

SmartCopier是一个用于高效处理Bean拷贝、转换的工具。

使用cglib asm库在运行时生成代码,兼顾运行效率和开发效率。
其性能是BeanUtil.copyProperties的200-1000倍,等同于硬编码。

比cglib的beanCopier提供了额外的设置功能,如默认值,是否用null覆盖,是否忽视null属性等,以及设置特殊的属性对应关系等等。

此外,提供更加灵活的转换器,允许只对部分属性进行转换,且不会对其他属性带来额外开销。

实现原理:

会在运行时生成一个实现类,该实现类实现Copier接口:

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

<dependency>
    <groupId>io.github.swqxdba</groupId>
    <artifactId>smart-copier</artifactId>
    <version>0.0.9</version>
</dependency>

2 使用

static SmartCopier smartCopier = new SmartCopier();
public static void main(String[] args) {
    PersonDto personDto; //...
    Person person; //...
    smartCopier.copy(person, personDto);
}

详细介绍

Copier

Copier是一个接口,定义了三个方法:

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是线程安全的。

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。

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对象是不同的。

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实例。

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<B> example2(List<A> list){
    SmartCopier smartCopier = new SmartCopier();
    return smartCopier.copyToList(list,B.class);
}

在内部,会通过一个ConcurrentHashMap来获取生成的Copier实例执行拷贝。

Bean

SmartCopier是Bean属性拷贝和转换的工具,依靠Getter、Setter来寻找类中的属性。
除了普通的set get方法外,还会识别出带有返回值的set方法。

A setName(String name){
    this.name=name;
    return this;
}

类型转换

在拷贝/转换的过程中,会遇到不一样类型的同名属性,比如:

class Dto{
    List<SubDto> children;
}

class Entity{
    List<SubEntity> children;
}

在一些BeanUtil中,只使用class.isAssignableFrom来判断是否能赋值。 但是这么判断不会判断集合元素的内部类型,会造成堆污染。
SmartCopier会执行递归检测,以保证其中的泛型实参也能够互相兼容。如果不兼容,则需要提供转换器进行转换处理,否则不会对该属性进行转换。

在上面的例子中,需要提供一个类型转换器,提供SubDto和SubEntity的转换逻辑。
你可以通过CopyConfig来配置你的转换器。

CopyConfig config = new CopyConfig();
config.addConverter(yourTypeConverterProvider);

TypeConverterProvider

TypeConverterProvider是一个转换器工厂,用于生成转换器实例。每个转换器负责从一个类型到另一个类型的转换。

而TypeConverterProvider负责生成这些转换器。这么设计的好处是,你可以在转换器对象内部保存一些状态。

一个常见的类型转换器是PackageBasedTypeConverterProvider, 它使用一个SmartCopier来对某个包名下的所有类型进行转换。

你可以把项目所在的包名传进去,让其转换项目中的各种类型。

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的转换。

class A{
    List<Integer> list;
}
class B{
    Set<Integer> list;
}
class C{
    Integer[] list;
}
class D{
    int[] list;
}
class D{
    Collection<Integer> list;
}
注意 当你想把一个List<Integer>转换到int[],且前面的List中存在null元素时,相应的位置会是元素的默认值。  
比如有 List<Integer> = [1,2,null,4]
转换到int[]后会变成 [1,2,0,4]

SmartCopier指的基础类型默认值,是基础类型数组元素的默认值,比如 (new int[1])[0]

About

运行时生成bean copier代码的高效率工具。 使用cglib asm库在运行时生成代码,兼顾运行效率和开发效率。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published