1. はじめに
今回はSpring FrameworkのBeanWrapper
を利用して、文字列で指定したプロパティに動的にアクセスする方法について紹介したいと思います。
BeanWrapper
について簡単に説明すると「Setter,Getterを使わずに、プロパティの値を設定したり取得したりするAPI」です。
プロパティに動的にアクセスするライブラリというと「Apache Commons BeanUtils」を思い浮かべる人もいるかと思いますが、同じような目的の機能です。
1.1. BeanWrapperとは?
Spring Framework(Core)の公式ガイドライン「3.4. Bean manipulation and the BeanWrapper」でもしっかりと紹介されています。
基本的な使い方は以下の通りです。
-
BeanWrapper
のインスタンスを生成する -
setPropertyValue
メソッドでプロパティに値を設定する -
getPropertyValue
メソッドでプロパティの値を取得する - プロパティは決められたルール(Expression)で指定する
プロパティを指定するルール(Expression)も公式ガイドラインに記載されています。
Table 11. Examples of properties
Expression | Explanation |
---|---|
name | Indicates the property name corresponding to the methods getName() or isName() and setName(..)
|
account.name | Indicates the nested property name of the property account corresponding e.g. to the methods getAccount().setName() or getAccount().getName()
|
account[2] | Indicates the third element of the indexed property account. Indexed properties can be of type array , list or other naturally ordered collection |
account[COMPANYNAME] | Indicates the value of the map entry indexed by the key COMPANYNAME of the Map property account |
基本的な使い方は説明されていますが、残念なのはコレクション(account[2]
やaccount[COMPANYNAME]
)を操作するサンプルが記載されていないことです。
今回はこのコレクションのサンプルについて説明したいと思います。
2.1. BeanWrapperの使い方
コレクションのプロパティを操作する場合、操作する深さによって使い方が異なります。今回は以下の2つのパターンについて説明したいと思います。
- コレクションに格納する要素を設定する
- コレクションに格納されている要素のプロパティに値を設定する
package demo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
public class BeanWrapperDemo {
public static void main(String[] args) {
// コレクションに格納する要素を設定する
test01();
// コレクションに格納されている要素のプロパティに値を設定する
test02();
}
// ★ポイント1
private static void setPropertyValues(Object target, Map<String, Object> input) {
BeanWrapper wrapper = new BeanWrapperImpl(target);
for (String key : input.keySet()) {
wrapper.setPropertyValue(key, input.get(key));
}
}
// コレクションに格納する要素を設定する
private static void test01() {
try {
// ★ポイント2
Team team = new Team();
List<Member> memberList = new ArrayList<>();
Map<String, Item> items = new HashMap<>();
team.setMemberList(memberList);
team.setItems(items);
Member taro = new Member();
taro.setMail("test@test.com");
taro.setName("TARO YAMADA");
Member hanako = new Member();
hanako.setMail("hanako@example.com");
hanako.setName("HANAKO TOYOSU");
Item notepc = new Item();
notepc.setItemName("ノートPC");
notepc.setPrice(100000);
Item book = new Item();
book.setItemName("Spring徹底入門");
book.setPrice(4000);
Map<String, Object> input = new HashMap<>();
input.put("teamId", "01234");
input.put("teamName", "HelloWorld");
input.put("point", 100);
// ★ポイント3
input.put("memberList[0]", taro);
input.put("memberList[1]", hanako);
input.put("items[A01]", notepc);
input.put("items[A02]", book);
setPropertyValues(team, input);
System.out.println(team);
} catch(Exception e) {
e.printStackTrace();
}
}
// コレクションに格納されている要素のプロパティに値を設定する
private static void test02() {
try {
// ★ポイント4
Team team = new Team();
List<Member> memberList = new ArrayList<>();
memberList.add(new Member());
memberList.add(new Member());
Map<String, Item> items = new HashMap<>();
items.put("A01", new Item());
items.put("A02", new Item());
team.setMemberList(memberList);
team.setItems(items);
Map<String, Object> input = new HashMap<>();
input.put("teamId", "01234");
input.put("teamName", "HelloWorld");
input.put("point", 100);
// ★ポイント5
input.put("memberList[0].mail", "test@test.com");
input.put("memberList[0].name", "TARO YAMADA");
input.put("memberList[1].mail", "hanako@example.com");
input.put("memberList[1].name", "HANAKO TOYOSU");
input.put("items[A01].itemName", "ノートPC");
input.put("items[A01].price", 100000);
input.put("items[A02].itemName", "Spring徹底入門");
input.put("items[A02].price", 4000);
setPropertyValues(team, input);
System.out.println(team);
} catch(Exception e) {
e.printStackTrace();
}
}
}
★ポイント1
BeanWrapper
のインスタンスを生成します。
org.springframework.beans.BeanWrapper
はインターフェースです。
実装クラスはorg.springframework.beans.BeanWrapperImpl
で、コンストラクタはいくつかありますが、今回はオブジェクトを引数に取るものを利用します。
値を設定するにはsetPropertyValue
メソッドを利用します。第1引数はプロパティを指定するルール(Expression)、第2引数は設定したい値です。
詳細についてはJavadocを参照してください。
- https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanWrapper.html
- https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanWrapperImpl.html
★ポイント2
「コレクションに格納する要素を設定する」場合、対象となるコレクションがインスタンス化されている必要があります。
コレクションの中身は不要です。
★ポイント3
コレクションに格納する要素を設定するには[]
でコレクションの要素の位置を指定します。
-
List(順序付のリスト、配列)
の場合、[]
に操作対象となるインデックス(0から開始)を指定します。 -
Map
の場合、[]
に操作対象となるキーを指定します。
★ポイント4
「コレクションに格納されている要素のプロパティに値を設定する」場合、対象となるコレクションのその要素までインスタンス化されている必要があります。
-
List(順序付のリスト、配列)
の場合、操作対象となるインデックスまで要素を追加してください。 -
Map
の場合、操作対象となるキーで要素を追加してください。
★ポイント5
[]
でコレクションの位置を指定した後、.プロパティ名
で設定対象となるプロパティを指定します。
対象となるコレクションの要素がインスタンス化されていない場合、例外が発生します。
そのため、事前に必ず★ポイント4で操作対象の要素をインスタンス化する必要があります。
2.2. (参考)モデルクラス(POJO)の定義
参考までに、入れ子のコレクションが分かるようにモデルクラスを記載します。
package demo;
import java.util.List;
import java.util.Map;
public class Team {
private String teamId;
private String teamName;
private int point;
private List<Member> memberList;
private Map<String, Item> items;
// setter,getter omitted
// override toString generated by eclipse
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Team [teamId=");
builder.append(teamId);
builder.append(", teamName=");
builder.append(teamName);
builder.append(", point=");
builder.append(point);
builder.append(", memberList=");
builder.append(memberList);
builder.append(", items=");
builder.append(items);
builder.append("]");
return builder.toString();
}
}
package demo;
import java.util.Map;
public class Member {
private String mail;
private String name;
private Map<String, String> memo;
// setter,getter omitted
// override toString generated by eclipse
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Member [mail=");
builder.append(mail);
builder.append(", name=");
builder.append(name);
builder.append(", memo=");
builder.append(memo);
builder.append("]");
return builder.toString();
}
}
package demo;
public class Item {
private String itemName;
private int price;
// setter,getter omitted
// override toString generated by eclipse
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Item [itemName=");
builder.append(itemName);
builder.append(", price=");
builder.append(price);
builder.append("]");
return builder.toString();
}
}
3. さいごに
今回はSpring FrameworkのBeanWrapper
を利用して文字列で指定したプロパティに動的にアクセスする方法について説明しました。
便利なBeanWrapper
ですが、処理性能についてはもちろん普通の'Setter,Getter'を利用した方がいいです。
BeanWrapper
の使いどころとしては、①動的アクセスが必要となる汎用的な処理や、②文字列でプロパティを指定できる利便性を利用してテストクラスで入力値を設定する等かと思います。