Commons Beanutils の PropertyUtils.setSimpleProperty() とか使ってて、おやっ、と不思議だったのでメモ。
謎な現象
以下のようなおかしな命名規則のBeanがあり、setSimpleProperty()にて値を設定した場合、
public class Bean { private String lll; // 全部小文字 private String UUU; // 全部大文字 private String Ull; // 先頭が大文字 public void setLll(String lll) { this.lll = lll; } public void setUUU(String uuu) { this.UUU = uuu; } public void setUll(String ull) { this.Ull = ull; } 以下略
以下はもちろん普通に上手くいき、Bean の該当プロパティに"hoge"が設定されます。
Bean b = new Bean(); PropertyUtils.setSimpleProperty(b, "lll", "hoge"); PropertyUtils.setSimpleProperty(b, "UUU", "hoge");
でも、
PropertyUtils.setSimpleProperty(b, "Ull", "hoge");
とすると、java.lang.NoSuchMethodException: Unknown property 'Ull' と例外になってしまいます。
そこで、先頭文字を小文字にすると、
PropertyUtils.setSimpleProperty(b, "ull", "hoge");
何事もなく上手くいきます。
setter 名を作るために、先頭文字を toUpperCase してるんだろうなぁと思い、以下を試すと
PropertyUtils.setSimpleProperty(b, "uUU", "hoge");
あれれ? java.lang.NoSuchMethodException: Unknown property 'uUU' 失敗します・・謎です。
そういえば、昔URLのパラメータで先頭文字が大文字だと上手くいかなかったからURLパラメータにフィルタかました・・ なんてのを聞いたのを思い出しつつ謎を追います。
謎を追う
意味不明なので Beanutils のソースを追います。
setSimpleProperty() の呼出は、内部で org.apache.commons.beanutils.PropertyUtilsBean を使用しています。PropertyUtilsBean 内部では、Beanのプロパティの管理にjava.beans.PropertyDescriptor を使用しており、PropertyDescriptorをキャッシュしていたりします。
ここからは、Beanutils の世界ではなく、Javaの標準APIの世界に入っていきます。
PropertyUtilsBean を取得するために使われているのが、java.beans.BeanInfo になり、BeanInfo は、Introspector.getBeanInfo() として取得されます。さらに深く追っていくと、Introspector.decapitalize() というメソッドで、Beanのプロパティ名を編集しています。このメソッドは以下の様になっており、プロパティ名のプレフィックスである"set"などを除去した文字列が渡されます。
public static String decapitalize(String name) { if (name == null || name.length() == 0) { return name; } if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){ return name; } char chars[] = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); }
なるほど、謎は解けました。
プロパティ名の先頭の2文字が大文字の場合は、そのままプロパティ名として利用。そうでない場合は先頭文字を小文字にしてプロパティ名とする、なんてことがされています。
つまり、getUUUの場合、先頭2文字が大文字なので、プロパティ名はUUU
getUllの場合、1文字目を小文字にして、ullがプロパティ名となります。