Beanをクライアントに返す直前に、Beanのプロキシを作成できます。クラスからプロキシを作成するには、 javassistが必要です(インターフェイスからプロキシを作成するには、Java SEを使用して直接実行できます)。
次に、「set」で始まるメソッドが呼び出された場合、例外をスローできます。
これは、「任意の」POJOをラップできるメソッドを持つ再利用可能なクラスです。
import java.lang.reflect.Method;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;
public class Utils {
public static <C> C createInmutableBean(Class<C> clazz, final C instance)
throws InstantiationException, IllegalAccessException {
if (!clazz.isAssignableFrom(instance.getClass())) {
throw new IllegalArgumentException("given instance of class "
+ instance.getClass() + " is not a subclass of " + clazz);
}
ProxyFactory f = new ProxyFactory();
f.setSuperclass(clazz);
f.setFilter(new MethodFilter() {
public boolean isHandled(Method m) {
// ignore finalize()
return !m.getName().equals("finalize");
}
});
Class c = f.createClass();
MethodHandler mi = new MethodHandler() {
public Object invoke(Object self, Method m, Method proceed,
Object[] args) throws Throwable {
if (m.getName().startsWith("set")) {
throw new RuntimeException("this bean is inmutable!");
}
return m.invoke(instance, args); // execute the original method
// over the instance
}
};
C proxy = (C) c.newInstance();
((Proxy) proxy).setHandler(mi);
return (C) proxy;
}
}
そしてここにサンプルコードがあります。従業員をあなたの豆にしましょう:
public class Employee{
private String name="John";
private String surname="Smith";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
};
ここでは、POJOのプロキシを作成し、そのゲッターを使用できるが、そのセッターは使用できないことを示すテストケースを示しています。
@Test
public void testProxy() throws InstantiationException, IllegalAccessException{
Employee aBean = new Employee();
//I can modify the bean
aBean.setName("Obi-Wan");
aBean.setSurname("Kenobi");
//create the protected java bean with the generic utility
Employee protectedBean = Utils.createInmutableBean(Employee.class, aBean);
//I can read
System.out.println("Name: "+protectedBean.getName());
System.out.println("Name: "+protectedBean.getSurname());
//but I can't modify
try{
protectedBean.setName("Luke");
protectedBean.setSurname("Skywalker");
throw new RuntimeException("The test should not have reached this line!");
}catch(Exception e){
//I should be here
System.out.println("The exception was expected! The bean should not be modified (exception message: "+e.getMessage()+")");
assertEquals("Obi-Wan", protectedBean.getName());
assertEquals("Kenobi", protectedBean.getSurname());
}
}