Beanのアクセサを自動生成する
では、このJavassistの機能を利用して、もっと具体的な処理を考えてみましょう。一つの例として、「JavaBeansのBeanクラスにアクセサを自動生成する」というものはどうでしょうか。
Beanクラスでは、通常、属性などの値をprivateフィールドとして用意しておき、それにアクセスするためのGetter/Setterといったアクセサをメソッドとして用意します。これは、自分で手書きでソースコードを書くのはちょっと面倒ですよね。そこで、自動生成するような処理を考えてみましょう。
ここでは、クラスにpublicフィールドを用意しておくと、それを元にアクセサの処理をさせます。以下のような形でBeanクラスを処理すればよいでしょう。
1.クラスに用意されているpublicフィールドをすべて取得する。
2.フィールドの名前とタイプを調べ、それにアクセスするためのGetter/Setterメソッドを追加する。
3.フィールドをprivateに変更する。
では、実際に処理のためのクラスを定義しましょう。ここでは、「BeanAlter」というクラスを作成し、その中に「addAccesor」というメソッドとして処理を用意することにしましょう。
※BeanAlter.javaのソースコード
package jp.tuyano.jasample;
import java.lang.reflect.*;
import javassist.*;
import javassist.Modifier;
public class BeanAlter {
public static boolean addAccesor(String s){
ClassPool pool = ClassPool.getDefault();
CtClass cc = null;
boolean result = false;
try {
cc = pool.get(s);
CtField[] flds = cc.getFields();
for(CtField fld: flds){
String fldname = fld.getName();
String fldtype = fld.getType().getName();
String getter = getGetter(fldname,fldtype,cc);
String setter = getSetter(fldname,fldtype,cc);
System.out.println("getter:" + getter);
System.out.println("setter:" + setter);
if (getter != null){
cc.addMethod(CtNewMethod.make(getter,cc));
CtField field = new CtField(fld.getType(),fldname, cc);
field.setModifiers(Modifier.PRIVATE);
cc.removeField(fld);
cc.addField(field);
}
if (setter != null){
cc.addMethod(CtNewMethod.make(setter,cc));
}
}
cc.setSuperclass(cc.getSuperclass());
cc.writeFile();
result = true;
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch(Exception e){
e.printStackTrace();
}
return result;
}
private static String getGetter(String name,String type,CtClass cc){
String result = null;
String nstr = toUpLower(name);
try {
CtMethod m = cc.getDeclaredMethod("get" + nstr);
} catch (NotFoundException e) {
result = "public " + type
+ " get" + nstr + "(){"
+ "return " + name + ";"
+ "}";
}
return result;
}
private static String getSetter(String name,String type,CtClass cc){
String result = null;
String nstr = toUpLower(name);
try {
CtMethod m = cc.getDeclaredMethod("set" + nstr);
} catch (NotFoundException e) {
result = "public void"
+ " set" + nstr + "(" + type + " s){"
+ name + " = s;"
+ "}";
}
return result;
}
private static String toUpLower(String s){
byte[] sarr = s.getBytes();
sarr[0] = (byte)Character.toUpperCase((char)sarr[0]);
return new String(sarr);
}
}
このaddAccesorでは、調べるクラスの完全修飾名をStringとして引数に渡して呼び出します。これにより、そのクラスにあるpublicフィールドをすべてprivateに変更し、更にそのフィールドにアクセスするためのアクセサを作成します。作成できればtrueを、作成に失敗するとfalseを返すようにしています。
Getter/Setterメソッドのテキストは、getGetter/getSetterメソッドでそれぞれ作成しています。これらのメソッドでは、「getDeclaredMethod」というメソッドを利用しています。これは、引数に指定したメソッドがCtClassにあるかどうかチェックするものです。なければNotFoundExceptionという例外が発生します。これを利用し、NotFoundExceptionが発生したらまだメソッドが存在しないものとして、新たにメソッドのStringを生成してreturnさせています。
また、toUpLowerは、最初の1文字だけが大文字であとが小文字のStringを作成し返すものです。Getter/Setterは、例えば「test」というフィールドならば「getTest」「setTest」というような名前になりますから、1文字目だけを大文字にしたテキストを取得し、"set" + "Test"というようにしてメソッド名を得ればよい、というわけです。