CommonsとSpringのBeanUtils.copyProperties
Commons BeanUtils に Bean のプロパティをコピーする BeanUtils.copyProperties() というなかなか便利なメソッドがありますが、同名のメソッドが Spring にもあります。 (当然ながらパッケージ名は異なります)
ちなみに Seasar2 にも BeanUtil.copyProperties() というものがあります。
ここで、Commons のものと Spring のもので引数の順序が逆になっていたりと紛らわしい違いがあるので簡単にまとめてみました。
分類 | 第一引数 | 第二引数 | 例外 | MapからBeanへのコピー |
---|---|---|---|---|
Commons | コピー先 | コピー元 | 検査例外あり | ○ |
Spring | コピー元 | コピー先 | 実行時例外のみ | × |
パッケージ名や throws 定義されている例外は下記の通りです。
- Commons 版
- パッケージ: org.apache.commons.beanutils
- 例外: IllegalAccessException(検査例外), InvocationTargetException(検査例外)
- Spring 版
- パッケージ: org.springframework.beans
- 例外: BeansException(実行時例外)
また、Commons 版では Map から Bean へのプロパティコピーを実施できますが、Spring 版ではできない模様です。
そして、Commons 版では BigDecimal へ null 値をコピーするとデフォルトで下記のような例外が発生します。
BigDecimal へ null 値をコピーした場合の例外 (Commons 版)
org.apache.commons.beanutils.ConversionException: No value specified for 'BigDecimal'
これを防止するには ConvertUtils.register() を使って BigDecimalConverter を登録しておきます。
BigDecimal へ null 値をコピーするための対策 (Commons 版)
ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class)
動作確認
下記のようなサンプルスクリプトを使って挙動の違いを確認してみました。
copyproperties_sample.groovy
@Grab('commons-beanutils:commons-beanutils:1.8.3') @Grab('org.springframework:spring-beans:3.2.4.RELEASE') import org.apache.commons.beanutils.ConvertUtils import org.apache.commons.beanutils.converters.BigDecimalConverter import groovy.transform.* @TupleConstructor @ToString(includeNames=true) class Data { String name BigDecimal value } // Commons 版 BeanUtils.copyProperties def copyPropsCommons = { data -> println '----- Commons -----' try { def trg = new Data() org.apache.commons.beanutils.BeanUtils.copyProperties(trg, data) println trg } catch (e) { println e } } // Commons 版 BeanUtils.copyProperties with converter def copyPropsCommons2 = { data -> println '----- Commons with converter -----' def trg = new Data() // null を BigDecimal のプロパティへコピーするための設定 ConvertUtils.register(new BigDecimalConverter(null), BigDecimal) org.apache.commons.beanutils.BeanUtils.copyProperties(trg, data) // BigDecimalConverter の解除 ConvertUtils.deregister() println trg } // Spring 版 BeanUtils.copyProperties def copyPropsSpring = { data -> println '----- Spring -----' def trg = new Data() org.springframework.beans.BeanUtils.copyProperties(data, trg) println trg } def procList = [copyPropsCommons, copyPropsCommons2, copyPropsSpring] def d1 = new Data('sample1', 100) procList.each { it(d1) } println '' def d2 = new Data('sample2') procList.each { it(d2) } println '' def d3 = [ name: 'sample3', value: 10 ] // Commons 版は Map からのコピーが可能 copyPropsCommons(d3) // Spring 版は Map からのコピーが不可 copyPropsSpring(d3)
実行結果は下記のようになりました。
実行結果
> groovy copyproperties_sample.groovy ----- Commons ----- Data(name:sample1, value:100) ----- Commons with converter ----- Data(name:sample1, value:100) ----- Spring ----- Data(name:sample1, value:100) ----- Commons ----- org.apache.commons.beanutils.ConversionException: No value specified for 'BigDecimal' ----- Commons with converter ----- Data(name:sample2, value:null) ----- Spring ----- Data(name:sample2, value:null) ----- Commons ----- Data(name:sample3, value:10) ----- Spring ----- Data(name:null, value:null)
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20131013/