Apache Jakartaプロジェクトで作られた、JavaBeansを扱うユーティリティー。
Strutsが結構使っているようだ。
|
クラスパスが通っている場所にjarファイルを置くか、クラスパス(ビルドパス)にjarファイルの指定を追加する。
また、実行にはcommons-loggingが必要。
(見つからない場合、java.lang.NoClassDefFoundError:
org/apache/commons/logging/LogFactory
という例外が発生する)
Eclipse3.2を使っている場合、バージョン1.6のBeanUtilsがプラグインに入っている。
C:\eclipse\plugins\org.eclipse.tomcat_4.1.130.v20060601\commons-beanutils.jar
commons-collections.jar
JavaBeansを扱うに当たり、主にBeanUtilsとPropertyUtilsというクラスがある(BeanだけじゃなくてMapやDynaBeanにも対応している)。
PropertyUtilsは基礎的なユーティリティー。
BeanUtilsは高機能で、内部ではPropertyUtilsを使用している箇所もある。デバッグログへの出力も実装されている(だからloggingが必要になってしまう訳だが…)。
メソッド | 戻り値の型 | 概要 | 備考(相違点) | |
---|---|---|---|---|
BeanUtils | PropertyUtils | |||
setProperty(bean, name, value) |
void | void | bean内のnameにvalueをセットする。 | nameには、単純なプロパティー名の他に、「arr[0] 」といった配列の要素指定や、「sub.value 」といった別オブジェクトのプロパティーを指定することが出来る。→プロパティー名のルール |
getProperty(bean, name) |
String | Object | bean内のnameの値を返す。 | PropertyUtilsでは、元のbeanの型そのまま(プリミティブはラッパークラス)で値を返す。 BeanUtilsでは、PropertyUtilsを利用して値を取得し、Converterを使ってStringに変換してそれを返す。 |
copyProperties(dst, src) |
void | void | src内の各値をdstへセットする。 | コピー元とコピー先でプロパティーの型が異なる場合、BeanUtilsではConverterを使ってコピー先の型に変換するが、PropertyUtilsでは互換性の無い型だったら例外が発生する。 |
describe(bean) |
Map | Map | bean内の各値をMapに入れて返す。 | 引数のbeanがnullのとき、PropertyUtilsは例外を発生させるがBeanUtilsは空のマップを返す。 |
populate(bean, map) |
void | - | map内の各値をbeanに入れる。 | |
cloneBean(bean) |
Object | - | beanのクローン(複製)を作成する。 | beanのクラスを取得してnewInstance()でインスタンス生成してcopyProperties()で値をコピーしているので、clone()を実装する必要が無い。 |
このユーティリティー内部では、java.beansパッケージのPropertyDescriptorクラス等を使っている。
リフレクションの塊のようなものなので処理は重そうだが、クラス毎に情報をキャッシュしているので、繰り返し使う場合は負荷が軽減される。
BeanUtils#getProperty()では、本来の型をStringに変換した値を返す。また、BeanUtils#copyProperties()も、型が異なる場合はコピー先の型に変換する。
これらの変換はConvertUtilsというクラスが保持しているConverterクラスを用いて行われる。Converterは変換先の型に応じた具象クラスが用意されて
いてデフォルトで使われており、自分で作ることも出来る。
BeanUtils1.7では、BeanUtilsの各メソッド(static)は、ことごとくBeanUtilsBeanというオブジェクトの同名メソッド(非static)を呼び出すようになっている(委譲している)。
(BeanUtils1.6まではBeanUtilsBeanというクラスは無く、BeanUtils内に直接ロジックが書かれていた模様)
PropertyUtilsも同様に、PropertyUtilsBeanのメソッドを呼んでいるだけ。
ConvertUtilsもConvertUtilsBeanのメソッドを呼んでいる。
BeanUtilsBeanのコンストラクターには、BeanUtilsBean自身が使用するPropertyUtilsBeanとConvertUtilsBeanのインスタンスも必要となる。
BeanUtilsは1つだけBeanUtilsBeanをインスタンス化するが、その際にPropertyUtilsBeanとConvertUtilsBeanもインスタンス化している。
PropertyUtilsとConvertUtilsは、“BeanUtilsが使用するBeanUtilsBean”で保持しているPropertyUtilsBeanとConvertUtilsBeanを取得して使っている。
独自コンバーターの登録には、ConvertUtils#register()というメソッドを使う。これを呼び出すと、デフォルトのConvertUtilsBeanインスタンスにコンバーターを登録することになる。
しかしこの方法では、BeanUtilsを使っている他のライブラリー(Struts等)の変換にも影響を与えてしまって好ましくない。
そこで、独自変換を行いたい場合は、BeanUtilsBeanを自分で生成して使用し、BeanUtilsは使わないようにするのが良い。
import org.apache.commons.beanutils.Converter; /** * @see StringConverter */ public class SampleStringConverter implements Converter { public Object convert(Class type, Object value) { // System.out.println(type); //当オブジェクトをString用として登録するので、typeは必ずStringのはず if (value == null) { return (String) null; } else if (value instanceof Date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); return sdf.format(value); } else { return value.toString(); } } }
コピー元(Date型)Beanサンプル:
public class DateBean { private Date date; public void setDate(Date dt) { date = dt; } public Date getDate() { return date; } }
コピー先(String型)Beanサンプル:
public class StringBean { private String date; public void setDate(String dt) { date = dt; } public String getDate() { return date; } }
コンバーターの登録・使用例:
DateBean src = new DateBean(); StringBean dst = new StringBean(); // srcに現在日時をセット src.setDate(new Date()); //●BeanUtilsを使ってdstへコピー BeanUtils.copyProperties(dst, src); System.out.println(dst.getDate()); //●独自BeanUtilsBeanを作ってデフォルト状態でdstへコピー BeanUtilsBean bu = new BeanUtilsBean( new ConvertUtilsBean(), //独自コンバーターを登録する為に新しいインスタンスを生成 BeanUtilsBean.getInstance().getPropertyUtils() //デフォルトのPropertyUtilsBeanを共用 ); bu.copyProperties(dst, src); System.out.println(dst.getDate()); //●独自BeanUtilsBeanに独自コンバーターをセットしてdstへコピー bu.getConvertUtils().register( new SampleStringConverter(), //独自コンバーターを String.class //String型への変換として登録 ); bu.copyProperties(dst, src); System.out.println(dst.getDate()); //●大元のBeanUtilsには影響してないことを確認 BeanUtils.copyProperties(dst, src); System.out.println(dst.getDate());
実行結果:
Wed Dec 05 23:05:14 JST 2007 ←BeanUtilsによるコピー Wed Dec 05 23:05:14 JST 2007 ←自分で生成したBeanUtilsBeanの初期状態によるコピー 2007/12/05 23:05:14 ←独自コンバーターを使用したコピー Wed Dec 05 23:05:14 JST 2007 ←BeanUtilsによるコピー(独自コンバーターの影響を受けていない)
参考: Reo MIZOGUCHIさんのwww.Javable.Jp
- Notes:BeanUtils で独自コンバーターを使用する場合の落とし穴
Converterの登録時にsynchronized(ConvertUtils.class)を使
う方法を紹介しているけど、StrutsがConvertUtils.classで排他しているのでない限り、同期オブジェクトはFastHashMap辺りにしないと意味ないんじゃ…?
ついでに言うと、コンバーターをデフォルトに戻すConvertUtils#deregister()は全コンバーターオブジェクトを作り直すので、けっこう重そう…。