Bean内の全フィールドを取得するために試行錯誤した過程を備忘録として残します。
やりたいこと
Bean内の全フィールドをIteratorで取得したい。
以下のBeanであれば**「Field1、Field2、Field3、Field4」**が取得できること。
public class Fields {
// フィールド
private String Field1;
private String Field2;
private String Field3;
private String Field4;
// コンストラクタ
Fields(String Field1, String Field2, String Field3, String Field4) {
this.Field1 = Field1;
this.Field2 = Field2;
this.Field3 = Field3;
this.Field4 = Field4;
}
// settet,getterは省略
// .
// .
// .
}
やってみたこと 1
Javaの外部ライブラリである**「Apache Commons」のPropertyUtils/BeanUtilsクラス**を利用して取得
PropertyUtils及びBeanUtilsはJavaBeansを扱うクラスです。1
その中でもbean内の各値をMapに変換して返却するdescribe(Object bean)
メソッドを利用して、
**BeanをMapに変換 → 変換したMapのkey値を取得(フィールド名) **
といった流れでBean内の全フィールドを取得してみることにしました。
メソッド
// PropertyUtils#describeを用いて全フィールドを取得
public Iterator<?> getNames()
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
try{
// BeanをMapに変換 → 変換したMapのkey値を取得(フィールド名)
return PropertyUtils.describe(this).keySet().iterator();
}catch(IllegalAccessException e){
throw e;
}catch(InvocationTargetException e){
throw e;
}catch(NoSuchMethodException e){
throw e;
}
}
// BeanUtilsUtils#describeを用いて全フィールドを取得
public Iterator<?> getNames1()
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
try{
// BeanをMapに変換 → 変換したMapのkey値を取得(フィールド名)
return BeanUtils.describe(this).keySet().iterator();
}catch(IllegalAccessException e){
throw e;
}catch(InvocationTargetException e){
throw e;
}catch(NoSuchMethodException e){
throw e;
}
}
実行クラス
package test;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
public class FieldGet {
public static void main (String args[]){
Fields field = new Fields("フィールド1","フィールド2","フィールド3","フィールド4");
Iterator<?> propertyUtils;
Iterator<?> beanUtils;
try {
// PropertyUtils#describeを用いて全フィールドを取得
propertyUtils = field.getNames();
// BeanUtilsUtils#describeを用いて全フィールドを取得
beanUtils = field.getNames1();
System.out.println("--PropertyUtils利用--");
while(propertyUtils.hasNext()){
System.out.println(propertyUtils.next());
}
System.out.println("--BeanUtils利用--");
while(beanUtils.hasNext()){
System.out.println(beanUtils.next());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
実行結果
--PropertyUtils利用--
Field1
Field3
class
Field2
Field4
--BeanUtils利用--
Field1
Field3
class
Field2
Field4
Beanのフィールド名を取得することが出来ましたが、
フィールド名ではない「class」が取得されたり、順番が昇順でないなど、改善の余地がありそうです。
また、何度か実行しているうちに・・。
PropertyUtils/BeanUtils.describe(this)
の箇所でInvocationTargetException
が発生してしまい、フィールドが取得できなくなってしまいました。
詳細な原因がわからないため、別の方法でフィールド名を取得を検討することにしました。
Caused by: java.lang.reflect.InvocationTargetException
... 1024 more
Caused by: java.lang.StackOverflowError
やってみたこと 2
リフレクションを利用
Beanのフィールドに関してはリフレクションAPIでも取得が可能です。2
getDeclaredFields()
を用いると、すべてのアクセス修飾子(public、protected、デフォルト、private)のフィールドがFieldクラスの配列で取得可能です。
メソッド
// Class#getDeclaredFieldsを用いて全フィールドを取得
public Iterator<?> getNames2(){
// すべてのアクセス修飾子のフィールドを取得
Field[] tmpField = this.getClass().getDeclaredFields();
// Iteratorで返却するためのリストを生成
List<String> Field = new ArrayList<String>();
for(int i = 0; i < tmpField.length; i++) {
// getName()でフィールドを取得
Field.add(tmpField[i].getName());
}
return Field.iterator();
}
実行クラス
package test;
import java.util.Iterator;
public class FieldGet {
public static void main (String args[]){
Fields field = new Fields("フィールド1","フィールド2","フィールド3","フィールド4");
terator<?> reflection;
// Class#getDeclaredFieldsを用いて全フィールドを取得
reflection = field.getNames2();
System.out.println("--reflection利用--");
while(propertyUtils.hasNext()){
System.out.println(propertyUtils.next());
}
}
}
実行結果
--reflection利用--
Field1
Field2
Field3
Field4
これで想定どおり、全フィールドを取得することができました。
番外編
StrutsのDynaActionFormで全フィールドを取得
StrutsのDynaActionFormでも、全フィールドを取得することが可能です。
✳︎考え方としては「やってみたこと 1」と同様
手順1:DynaValidatorFormまたはDynaActionFormを継承したクラスを作成
手順2:「やってみたこと 1」と同様の考え方**(フォームをMapに変換 → 変換したMapのkey値を取得(フィールド名))**で、メソッドを実装
StrutsのDynaActionFormには、DynaActionFormで定義したプロパティをMap型に変換して返却するgetMap()
メソッドが存在する為、そのメソッドを利用すると「やって見たこと 1」と同様の考え方で、全フィールドを取得することが可能です。
PropertyUtils/BeanUtilsクラスのdescribe(Object bean)メソッドに相当するといったところでしょうか。
内部の詳細な処理についてはよくわかりませんが・・。
package test;
import java.util.Iterator;
import org.apache.struts.validator.DynaValidatorForm;
public class StrutsDynaValidatorForm extends DynaValidatorForm {
public Iterator getNames() {
return this.getMap().keySet().iterator();
}
}
-
BeanUtilsの使い方に関して参考になるサイトはこちら。Java BeanUtilsメモ(Hishidama's commons-BeanUtils Memo) ↩