Вы можете создать прокси-сервер для ваших бобов непосредственно перед возвращением их клиенту. Вам понадобится 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());
}
}
Это худшая идея, которую когда-либо задумывала человечество. Как именно это может принести пользу кому-либо? Кто хочет классы, которые неожиданно бросают исключения во время выполнения? То, что требовал оригинальный плакат, было неизменным классом по дизайну, а не FUD. –
Я согласен с вами. Лучше было бы избавиться от сеттеров статически. Мое решение пытается создать обертки без изменения сгенерированных компонентов и без необходимости писать больше кода. Кроме того, вы могли видеть такую плохую идею в стандартном Java-API: http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util. List) или в API коллекций Google: https://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableList.html#set(int, E) Оба эти бросая исключения во время выполнения. – lipido