文档章节

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

罗格林
 罗格林
发布于 06/14 13:37
字数 1734
阅读 2241
收藏 59
点赞 9
评论 13

本篇是 Java OSGL 工具库系列的第六篇, 前面五篇分别是:

  1. 图片处理的艺术
  2. 图片处理的艺术之自定义图片处理器
  3. 字符串操作的艺术
  4. I/O 操作的艺术
  5. 类型转换的艺术

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:
  • $.copy(foo).to(bar)
  1. DEEP_COPY- 深拷贝: 递归拷贝过程直到遇到不可变 (Immutable) 类型. API:
  • $.deepCopy(foo).to(bar)
  1. MERGE - 融合: 和深拷贝类似, 但碰到数组时容器的情况将源数据添加到目标容器. API:
  • $.merge(foo).to(bar)
  1. MAP - 印射: 和深拷贝类似, 并支持数据类型转换. API:
  • $.map(foo).to(bar)
  1. MERGE_MAP - 融合印射: 和融合类似, 并支持数据类型转换. API:
  • $.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 匹配. 当采用这种匹配方式的时候, 下面的名字被认定为相匹配的名字:
  • foo_bar
  • foo-bar
  • fooBar
  • FooBar
  • Foo-Bar
  • Foo_Bar
  1. 特殊匹配

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

$.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.15.1

© 著作权归作者所有

共有 人打赏支持
罗格林

罗格林

粉丝 254
博文 38
码字总数 37360
作品 4
其他
架构师
加载中

评论(13)

罗格林
罗格林

引用来自“衣舞晨风”的评论

有源码分享吗?
https://gitee.com/osglworks/java-tool
衣舞晨风
衣舞晨风
有源码分享吗?
罗格林
罗格林

引用来自“开源中国首席PHP宣传专家”的评论

OSGL 不是做模块化java的吗?可以用来模块化吗?

你说的是 OSGI, 不是 OSGL
开源中国首席罗纳尔多
开源中国首席罗纳尔多
OSGL 不是做模块化java的吗?可以用来模块化吗?
李嘉图
李嘉图
堪称完美,细节到位,完全对得起艺术这两个字
快速开发师
快速开发师
无论写的多好,都是增加学习成本,增加开发者的负担
罗格林
罗格林

引用来自“可爱的鱼”的评论

呃,能解释一下这个OSGL吗?百度搜不出来。
osgl - Open Source General Library 是一套开源工具库. 目前绝大部分都是 Java 的, 只有一个状态机工具是 C 的. 这个工具库的 repo 在 https://github.com/osglworks/, gitee 上也有, 只是目前 Gitee 上还不完全: https://gitee.com/osglworks
可爱的鱼
可爱的鱼
呃,能解释一下这个OSGL吗?百度搜不出来。
Artlongs
Artlongs
起初我抱怨 ACT 没有基本的bean copy 功能,提了个需求,想不到大神搞了个这么牛的工具类,666.
大神请收下我的膝盖。
罗格林
罗格林

引用来自“talent-tan”的评论

罗总写了这么多好博客,是不是该考虑一下出本书,我第一个买
谭总过奖了. 写书沉淀还差了些啊, 还需要多积累 ...
act-starters-1.8.8.6 发布 - 更加易用的 AAA 集成方案

ActFramework 是一款专注于代码表达力的高性能 Java MVC/RESTful 全栈框架. 主要更新: 更加易用的 AAA (认证, 授权, 记账) 集成 - 无需再写 Adaptor 类 全面升级的 e2e 支持 - 包括自动继承 ...

罗格林 ⋅ 06/22 ⋅ 0

Oracle Java Mission Control 帮助

缩写 含义 JDK Java 开发工具包 JDP Java Discovery Protocol JFR Java 飞行记录器 JMC Java Mission Control JMX Java Management Extensions JVM Java 虚拟机 MBean 托管 Bean (Java) RCP ......

光斑 ⋅ 04/27 ⋅ 0

Java并发编程之ThreadLocal源码分析

多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的,多线程访问同一个共享变量特别容易出现并发问题,特别是多个线程需要对一个...

狂小白 ⋅ 06/03 ⋅ 0

IOC/AOP工具 - jBeanBox

jBeanBox是一个微形但功能较齐全的IOC/AOP工具适用于JAVA7+,利用了Java的初始化块实现的Java配置代替XML。jBeanBox采用Apache License 2.0开源协议。 其他一些IOC/AOP框架的问题: 1)Sprin...

yong9981 ⋅ 2016/07/25 ⋅ 14

JNI开发流程与引用数据类型的处理

今天我们来看下Java JNI,先看下维基百科给的定义, JNI, Java Native Interface, Java本地接口,是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用或库,也可以被其他程序调用。...

juexingzhe ⋅ 05/04 ⋅ 0

叮!您收到一份超值Java基础入门资料!

摘要:Java语言有什么特点?如何最大效率的学习?深浅拷贝到底有何区别?阿里巴巴高级开发工程师为大家带来Java系统解读,带你掌握Java技术要领,突破重点难点,入门面向对象编程,以详细示例...

聒小小噪 ⋅ 05/12 ⋅ 0

java面试必备之ThreadLocal

按照传统的经验,如果某个对象是非线程安全的,在多线程环境下对象的访问需要采用synchronized进行同步。但是模板类并未采用线程同步机制,因为线程同步会降低系统的并发性能,此外代码同步解...

编程老司机 ⋅ 05/16 ⋅ 0

Java 编程之美:并发编程高级篇之一

本文来自作者 追梦 在 GitChat 上分享 「Java 编程之美:并发编程高级篇之一」 编辑 | 工藤 前言 借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了。 ...

gitchat ⋅ 05/24 ⋅ 0

SWIG与JAVA 交互最全开发指南一

项目背景 最近开始研究做移动端项目,但是本人基本是做了五六年的c++的底层研发,对C++的研发可以说是驾轻就熟了,但是对于android还是属于刚入门阶段,虽然断断续续做移动端也做了一年,但是...

揽月凡尘 ⋅ 06/16 ⋅ 0

编写你的第一个HelloWorld

写在前面的话 因为Java基础是以后学习框架的基石,因此开个文集首先写写Java基础,本来想直奔基础知识的介绍,但是为了保证知识的完整性,因此从Java安装和运行“hello world”开始(虽然百度...

nanaFighting ⋅ 06/15 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Linux中的端口大全

1 被LANA定义的端口 端口 名称 描述 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat 用于列举连接了的端口的系统状态 13 d...

寰宇01 ⋅ 15分钟前 ⋅ 0

Confluence 6 如何备份存储文件和页面信息

备份的 ZIP 文件包含有 entities.xml,这个 XML 文件包含有 Confluence 的所有页面内容和存储附件的目录。 备份 Zip 文件结构 页面的附件是存储在附件存储目录中的,通过页面和附件 ID 进行识...

honeymose ⋅ 18分钟前 ⋅ 0

【每天一个JQuery特效】根据状态确定是否滑入或滑出被选元素

主要效果: 本文主要采用slideToggle()方法实现以一行代码同时实现以展开或收缩的方式显示或隐藏被选元素。 主要代码如下: <!DOCTYPE html><html><head><meta charset="UTF-8">...

Rhymo-Wu ⋅ 21分钟前 ⋅ 0

度量.net framework 迁移到.net core的工作量

把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持。两个类库的差异性,通过人工很难识别全。好在微软的工程师们考虑到了我们顾...

李朝强 ⋅ 27分钟前 ⋅ 0

请不要在“微服务”的狂热中迷失自我!

微服务在过去几年一直是一个非常热门的话题(附录1)。何为“微服务的疯狂”,举个例子: 众所周知,Netflix在DevOps上的表现非常棒。Netfix可以做微服务。因此:如果我做微服务,我也将非常...

harries ⋅ 28分钟前 ⋅ 0

oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

背景 6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化: 基于Spring Boot 2.0.X 不兼容 Spring Boot 1.5.X 期间踩过几个坑,分享出来给大伙,主要是关于...

冷冷gg ⋅ 58分钟前 ⋅ 0

OSChina 周一乱弹 —— 理发师小姐姐的魔法

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @冰冰棒- :分享田馥甄的单曲《My Love》 《My Love》- 田馥甄 手机党少年们想听歌,请使劲儿戳(这里) @Li-Wang :哎,头发又长了。。。又要...

小小编辑 ⋅ 今天 ⋅ 8

Kafka1.0.X_消费者API详解2

偏移量由消费者管理 kafka Consumer Api还提供了自己存储offset的功能,将offset和data做到原子性,可以让消费具有Exactly Once 的语义,比kafka默认的At-least Once更强大 消费者从指定分区...

特拉仔 ⋅ 今天 ⋅ 0

NEO智能合约之发布和升级(二)

接NEO智能合约之发布和升级(一),我们接下来说说智能合约的升级功能。 一 准备工作 合约的升级需要在合约内预先设置好升级接口,以方便在升级时调用。接下来我们对NEO智能合约之发布和升级...

红烧飞鱼 ⋅ 今天 ⋅ 0

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部