前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java OSGL 工具库 - Bean 拷贝的艺术

Java OSGL 工具库 - Bean 拷贝的艺术

作者头像
老码农
发布2019-03-12 15:30:46
6200
发布2019-03-12 15:30:46
举报
文章被收录于专栏:老码农专栏老码农专栏

1. API 一览

// shallow copy from `foo` to `bar`
$.copy(foo).to(bar);
// deep copy from `foo` to `bar
$.deepCopy(foo).to(bar);
// deep copy using loose name match
$.deepCopy(foo).looseMatching().to(bar);
// deep copy with filter
$.deepCopy(foo).filter("-password,-address.streetNo").to(bar);
// deep copy with special name mapping rule
$.deepCopy(foo)
    .map("id").to("no")
    .map("subject").to("title")
    .to(bar);
// merge data from `foo` to `bar`
$.merge(foo).to(bar);
// map data from `foo` to `bar`
$.map(foo).to(bar);
// map data from `foo` to `bar` using strict name match
$.map(foo).strictMatching().to(bar);
// merge map data from `foo` to `bar`
$.mergeMap(foo).to(bar);

2. 概念

OSGL 依赖于 Java 反射来获得 Bean 的内部结构. 和很多其他工具不同, OSGL 使用字段而不是 Getter/Setter 来获取内部数据

2.1 语义

OSGL 提供一下五种不同的 Bean 拷贝语义:

  1. SHALLOW_COPY - 浅拷贝: 拷贝第一层字段的引用. API:
  2. $.copy(foo).to(bar)
  3. DEEP_COPY- 深拷贝: 递归拷贝过程直到遇到不可变 (Immutable) 类型. API:
  4. $.deepCopy(foo).to(bar)
  5. MERGE - 融合: 和深拷贝类似, 但碰到数组时容器的情况将源数据添加到目标容器. API:
  6. $.merge(foo).to(bar)
  7. MAP - 印射: 和深拷贝类似, 并支持数据类型转换. API:
  8. $.map(foo).to(bar)
  9. MERGE_MAP - 融合印射: 和融合类似, 并支持数据类型转换. API:
  10. $.mergeMap(foo).to(bar)
2.1.1 不可变类型

不可变类型在 OSGL Bean 深度拷贝过程中是非常重要的概念. 当 OSGL 发现拷贝的数据类型为不可变时, 深度拷贝过程将终止, 并直接将数据引用拷贝到目标 Bean.

OSGL 认定以下类型为不可变类型:

  • 所有的基本数据类型及其包装类型
  • 字符串
  • 枚举
  • 所有使用 OsglConfig.registerImmutableClassNames API 注册的类型
  • 所有使用 OsglConfig.registerImmutableClassPredicate($.Predicate) API 注册的判定函数返回 true 的类型

2.2 名字匹配

OSGL 支持数据名字匹配

  1. 严格匹配, 源数据和目标数据的字段名必须完全一致. 这是默认匹配方式.
  2. Keyword 匹配, 也叫 loose 匹配. 当采用这种匹配方式的时候, 下面的名字被认定为相匹配的名字:
  3. foo_bar
  4. foo-bar
  5. fooBar
  6. FooBar
  7. Foo-Bar
  8. Foo_Bar
  9. 特殊匹配

下面是一段特殊匹配的示例代码:

$.deepCopy(foo)
    .map("id").to("no")
    .map("subject").to("title")
    .to(bar);

上面代码指示 OSGL 将 foo 对象的 id 字段拷贝到 bar 对象的 no 字段, 并将 foosubject 字段拷贝到 bartitle 字段.

2.3 过滤器

过滤器用来指定在拷贝过程中忽略某些字段. 下面是过滤器的一些例子:

  • -email,-password - 忽略 emailpassword 字段;其他所有字段都需要拷贝
  • +email - 仅拷贝 email 字段, 其他所有字段都不拷贝
  • -cc.cvv- 忽略 cc 字段对象的 cvv 字段, 其他所有字段都拷贝
  • -cc,+cc.cvv - 对于 cc 字段对象, 仅拷贝其 cvv 字段, 忽略其他所有字段

使用过滤器的 API:

$.deepCopy(foo).filter("-password,-address.streetNo").to(bar);

注意 过滤器匹配目标对象, 而非源对象

2.4 根类型

上面我们有提到 OSGL 依赖于字段来获得拷贝数据. 因为 Java 类型继承的原因, 获取字段是一个递归过程直到遇到 Object.class. 有时候我们希望递归过程更早结束, 这个时候可以指定根类型. 假设我们有下面的类:

public abstract class ModelBase {
    public Date _created;
}

假设拷贝源的类型是 ModelBase 的子类, 而你的 Dao 使用 ModelBase._created 来判断某个 Entity 是新建记录, 还是从数据库中加载的老记录. 这种情况下, 当你需要拷贝某个老记录 Bean 到一个新记录 Bean, 并使用 Dao 来保存这个新建记录的时候就需要注意不能拷贝 ModelBase._created 字段. 因此需要指定根类型:

MyModel copy = $.copy(existing).rootClass(ModelBase.class).to(MyModel.class);

2.5 目标的泛型

当目标对象是一个泛型, 例如容器时, 需要提供 targetGenericType 来完成拷贝:

List<Foo> fooList = C.list(new Foo(), new Foo());
List<Bar> barList = C.newList();
$.map(fooList).targetGenericType(new TypeReference<List<Bar>>(){}).to(barList);

2.6 类型转换

使用印射语义进行拷贝时, OSGL 自动在源数据类型和目标数据类型之间做转换. 假设源 Bean 定义为:

public class RawData {
    Calendar date;
    public RawData(long currentTimeMillis) {
        date = Calendar.getInstance();
        date.setTimeInMillis(currentTimeMillis);
    }
}

目标 Bean 定义为:

public static class ConvertedData {
    DateTime date;
}

当我们需要将 RawData 拷贝到 ConvertedData 时, 需要将源数据 dateCalendar 类型转换到目标数据 dateDateTime 类型. 开发可以写一个类型转换器:

public static Lang.TypeConverter<Calendar, DateTime> converter = new Lang.TypeConverter<Calendar, DateTime>() {
    @Override
    public DateTime convert(Calendar calendar) {
        return new DateTime(calendar.getTimeInMillis());
    }
};

并在 API 调用中指定类型转换器:

@Test
public void testWithTypeConverter() {
    RawData src = new RawData($.ms());
    ConvertedData tgt = $.map(src).withConverter(converter).to(ConvertedData.class);
    eq(tgt.date.getMillis(), src.date.getTimeInMillis());
}

注意

  • 在实际中你可能不需要定义很多类型转换器, 包括上面那个 CalendarDateTime 的, 因为 OSGL 已经默认提供了大部分需要用到的. 更多关于类型转换器的情况参见 类型转换的艺术
  • 类型转换仅适用于 MAPMERGE_MAP 语义, SHALLOW_COPY, DEEP_COPYMERGE 语义不支持类型转换
2.6.1 转换提示

有的情况需要给出转换提示来帮助类型转换正确进行. 一个典型的例子是从字符串转换为日期, 这个过程需要提供日期格式作为转换提示. 另一个例子是从字符串转换为整型, 可以提供 radix 转换提示来调整转换过程. 下面是一个字符串到日期类型转换的案例.

源 Bean 定义:

public static class RawDataV2 {
    String date;
    public RawDataV2(String date) {
        this.date = date;
    }
}

目标 Bean 定义:

public static class ConvertedDataV2 {
    Date date;
}

使用转换提示进行源 Bean 到目标 Bean 拷贝:

RawDataV2 src = new RawDataV2("20180518");
ConvertedDataV2 tgt = $.map(src).conversionHint(Date.class, "yyyyMMdd").to(ConvertedDataV2.class);

从代码中我们看到转换提示 yyyyMMdd 和目标数据类型 Date.class 绑定在一起, 这是告诉 OSGL, 当转换目标类型为 Date 的时候, 使用转换提示 yyyyMMdd.

2.7 实例工厂

在拷贝过程中, 有可能需要就某个类型创建实例. 默认情况下 OSGL 使用 org.osgl.Lang.newInstance(Class) API 来创建实例. 有的环境下, 例如当应用运行在 ActFramework 下的时候, 需要将实例创建交给容器 API Act.newInstance(Class). OSGL 提供 API 来注册实例工厂来替代默认创建实例的逻辑:

OsglConfig.registerGlobalInstanceFactory(new $.Function<Class, Object>() {
    final App app = Act.app();
    @Override
    public Object apply(Class aClass) throws NotAppliedException, $.Break {
        return app.getInstance(aClass);
    }
});

4. 总结

OSGL 提供了一套功能完善的 API 支持 Bean 的拷贝操作, 包括 5 种拷贝语义. 如果您喜欢 OSGL, 这里是 maven 坐标:

<dependency>
  <groupId>org.osgl</groupId>
  <artifactId>osgl-tool</artifactId>
  <version>${osgl-tool.version}</version>
</dependency>

目前最新的 ${osgl-tool.version}1.17.0

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018/06/14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. API 一览
  • 2. 概念
    • 2.1 语义
      • 2.1.1 不可变类型
    • 2.2 名字匹配
      • 2.3 过滤器
        • 2.5 目标的泛型
          • 2.6 类型转换
            • 2.6.1 转换提示
          • 2.7 实例工厂
          • 4. 总结
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档