Se puede crear un proxy para sus granos justo antes de devolverlas al cliente. Necesitará javassist para crear Proxies a partir de clases (crear proxies a partir de interfaces se puede hacer con Java SE directamente).
Luego, puede lanzar una excepción si se invocan los métodos que comienzan con "set".
Aquí es una clase reutilizable con un método que puede envolver "ninguna" 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;
}
}
Y aquí es un ejemplo de código. Deje empleado sea el bean:
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;
}
};
Y aquí un caso de prueba que demuestra que se puede crear un proxy para un POJO, utilizar sus captadores, pero no puede utilizar su setter
@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());
}
}
Esta es la peor idea que la humanidad haya concebido. ¿Cómo va a beneficiar esto a alguien? ¿Quién quiere clases que inesperadamente lanzan excepciones de tiempo de ejecución? Lo que el cartel original quería eran clases inmutables por diseño, no por FUD. –
Estoy de acuerdo con usted. Ocultando a los setters estáticamente sería una mejor solución. Mi solución intenta crear envoltorios sin cambiar los beans generados y sin necesidad de escribir más código. Además, se podría haber visto tan mala idea en la API estándar de Java: http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util. lista), o en el Google API Colecciones: https://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableList.html#set(int, E) Ambos arrojando excepciones de tiempo de ejecución. – lipido