在Java開發中,常常會有一個需求,將一個 Bean 複製到另一個 Bean,尤爲是在後臺分層的場景下,在不一樣的層之間傳遞信息,常常須要進行 這樣的一個對象複製工做,相似於:html
val source: PersonSource = ...val dest = new PersonDest() dest.setName( source.getName ) dest.setEmail( source.getEmail ) dest.setAddress( source.getAddress ) ...
由於這樣的代碼過於冗長,大量的這樣的代碼,大大的提高了代碼的複雜性,不只工做無趣,並且很容易遺漏,代碼可閱讀性差。萬能的程序員 天然就會發明一個又一個的輪子來解決這個問題:android
apache BeanUtilsgit
dozer:http://dozer.sourceforge.net程序員
orika:http://orika-mapper.github.io/github
全部這樣的輪子,都包括這樣的一些特性:sql
字段映射。 通常經過同名字段映射,或者使用 annotation 定義Bean間的字段映射。apache
類型映射。 諸如完成 String -> Int 這樣的類型轉換。編程
嵌套映射。json
BeanUtils是一個最簡單的,經過reflection來實現對象間的映射,這樣存在的問題是有很大的反射開銷,對性能要求很高的場景是不適合的。 orika等後續的框架則是經過字節碼生成等技術,來實現性能的提高。安全
當咱們走向函數式編程時,咱們也會面臨着對象的映射和複製的問題,BeanUtils/Orika等框架通常是基於Java Beans模型,而在函數式編程 中,咱們更傾向於使用 immutable 的對象,beanutils/orika等模型存在不匹配的狀況,因而,咱們構造了一款適合於函數式編程的對象複製 方式,即 BeanBuilder.
這是一個基本的使用方法:
val source = PersonSource( name = "wangzx", email = "wangzx@qq.com", address="here" )
val dest = BeanBuilder.build[PersonDest](source)()
上面的代碼中,咱們會返回一個PersonDest對象(也是一個不可變的Case Class),並完成對象屬性的複製工做,一個完整的使用方式以下:
def build[T](sources: Any*)(additions: (String, Any)*) : T
T
目標類型,必須是一個Immutable Case Class,BeanBuilder 設計上是一個從 Case Class -> Case Class的轉換工具
sources
數據來源。 能夠是0到多個對象。 這些對象的字段 會成爲咱們目標對象的字段 的來源。與BeanUtils、Orika等不的是, 咱們的目標對象能夠從多個源對象中複製屬性。
additions
以 "name" -> value
的形式定義的附加屬性設置,意思是將 value 複製到 目標對象的 name 屬性。
BeanBuilder 在設計上也不一樣於 BeanUtils,Orika,BeanBuilder首先被設計成爲精確的、類型安全的對象複製,體如今:
精確。BeanBuilder 目前限定是同名複製,並且必須沒有歧義。即值或者經過 additions 精確賦值,或者惟一存在於 sources 對象之中,即只有一個 source 對象有這個同名屬性。
全部目標對象中的非缺省值,都必須有數據來源,不能缺失(不會自動使用null做爲目標值)。
類型安全。 BeanBuilder不會自動的進行 String -> Int 的轉換,除非你主動的允許這個類型轉換。固然,在BeanBuilder中,咱們 能夠更簡單的定義源和目標的類型轉換能力,而且在一種類型安全的方式進行。
BeanBuilder的類型轉換採用如何策略,這裏,F
是源類型(值爲f
),T
是目標類型(值爲t
):
若是 F
是 T
的相同,或者子類型, 則老是成功的。t = f
若是存在一個隱式轉換,則老是成功的。t = implicit_convert(f)
若是 存在 t = f.copyTo[T]
是類型安全的,那麼,會使用這個轉換。 t = f.copyTo[T]
若是 F
和T
是容器類型 X[A]
和 X[B]
,而且 A
和 B
能夠知足上述的轉換關係,則 f = t.map ( A -> B )
若是 F
和 T
都是Case Class,而且能夠遞歸使用 t = BeanBuilder.build[T](f)()
若是單純根據如上的規則,可能不少的同窗會以爲,這麼複雜的一套規則,在Java中要麼是徹底作不到的(大部分),要麼是這會產生巨大的 運行時性能損耗,會存在嚴重的性能問題。那麼,最最神奇的事情發生了:
BeanBuilde會是同類框架中性能最優的,它可以達到數據複製的理論最快速度,在沒有任何性能損耗的狀況下,實現類型安全的對象複製
爲何做者能夠這麼自信的說呢?這就是Macro的神奇,與BeanUtils/Orika等不一樣的是, BeanBuilder會在編譯期間,完成全部的類型 檢查工做,完成源對象與目標對象之間的映射關係檢查和創建,爲你自動的編寫出Copy代碼,就像手工編寫的轉換代碼代碼同樣。你或者能夠 理解爲 BeanBuilder 是一個擴展了的 scalac 編譯特性,固然這一切創建在 scala 的標準語言特性之上。
通過前面系列的鋪墊,相信讀者已經具有了看懂這個代碼的基礎,BeanBuilder的所有源代碼也很簡單,約200行,具體內容就不在本文中 詳細陳述了,讀者若有興趣,能夠查看:https://github.com/wangzaixiang/scala-sql/blob/master/src/main/scala/wangzx/scala_commons/sql/BeanBuilder.scala
經過 BeanBuilder 這個案列,相信讀者可以感知到一個徹底不一樣的思惟模式,打開新的想象空間。而這,就是 元編程 的魅力之所在吧。
dapeng 2.0 即將全新發布,新版本將是對1.0版本的徹底重構,對簡化代碼結構的狀況下,提供了全新的異步支持、Scala服務開發支持。 咱們的主力工程師 @EmuuGrass(美女)、@jackliang 後續會準備很多的技術分享。
dapeng 2.0 將內置一個極速json解析器,dapeng-soa將在支持thrift binary/compact binary的基礎之上,提供極速的JSON服務。 等待@ever大神的新博文分享。
參考:
轉自: