LoginSignup
5
1

More than 3 years have passed since last update.

Beanの全フィールドを取得したい時に試したこと

Last updated at Posted at 2020-01-12

Bean内の全フィールドを取得するために試行錯誤した過程を備忘録として残します。

やりたいこと

Bean内の全フィールドをIteratorで取得したい。
以下のBeanであれば「Field1、Field2、Field3、Field4」が取得できること。

Fields.java
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及びBeanUtilsJavaBeansを扱うクラスです。1
その中でもbean内の各値をMapに変換して返却するdescribe(Object bean)メソッドを利用して、
BeanをMapに変換 → 変換したMapのkey値を取得(フィールド名) 
といった流れでBean内の全フィールドを取得してみることにしました。

メソッド

Fields.java(PropertyUtilsを利用)
 // 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;
    }
  }
Fields.java(BeanUtilsを利用)
  // 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;
        }
      }

実行クラス

FieldGet.java
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クラスの配列で取得可能です。

メソッド

Fields.java(リフレクションを利用)
  // 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();
    }

実行クラス

FieldGet.java
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)メソッドに相当するといったところでしょうか。
内部の詳細な処理についてはよくわかりませんが・・。

StrutsDynaValidatorForm.java
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();       
    }
}

  1. BeanUtilsの使い方に関して参考になるサイトはこちら。Java BeanUtilsメモ(Hishidama's commons-BeanUtils Memo) 

  2. JavaのリフレクションAPIでフィールド取得 

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1