Bạn có thể tạo một Proxy cho đậu của bạn ngay trước khi trả lại cho khách hàng. Bạn sẽ cần javassist để tạo Proxy từ các lớp (tạo proxy từ giao diện có thể được thực hiện trực tiếp với Java SE).
Sau đó, bạn có thể ném ngoại lệ nếu phương thức bắt đầu bằng "set" được gọi.
Dưới đây là một lớp học thể tái sử dụng với một phương pháp có thể quấn "bất kỳ" 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;
}
}
Và đây là một ví dụ mã. Hãy để nhân viên được đậu của bạn:
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;
}
};
Và đây là một trường hợp thử nghiệm cho thấy rằng bạn có thể tạo một proxy cho một POJO, sử dụng thu khí của nó, nhưng bạn không thể sử dụng setters nó
@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());
}
}
Đây là ý tưởng tồi tệ nhất mà nhân loại đã từng hình thành. Làm thế nào chính xác là điều này sẽ có lợi cho bất cứ ai? Ai muốn các lớp học bất ngờ ném ngoại lệ thời gian chạy? Những gì các poster ban đầu muốn được các lớp học bất biến theo thiết kế, không phải bởi FUD. –
Tôi đồng ý với bạn. Ẩn các setters tĩnh sẽ là một giải pháp tốt hơn. Giải pháp của tôi cố gắng tạo trình bao bọc mà không thay đổi các bean được tạo ra và không cần phải viết thêm mã. Ngoài ra, bạn có thể đã thấy ý tưởng tồi trong API Java chuẩn: http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util. Danh sách) hoặc trong API Bộ sưu tập của Google: https://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableList.html#set(int, E) Cả hai ném ngoại lệ thời gian chạy. – lipido