首先,什么是Proxy?这个可以参考一下GoF (著名的软件“四人帮”Gang of Four)提出的《设计模式》。简而言之,就是一个子类,在父类的基础上,实现一点附加功能,而将其他请求全都交给父类去实现,因而从用户角度看子类可以完成全部父类的功能以及子类自己附加的功能,所以可以代替父类使用。
Hibernate 使用 Proxy 实现 Lazy Loading 功能。 比如 Person 类有个成员,也是 Person 类,叫做 boss。如果使用 Lazy Loading,在加载一个 Person 的时候,不会自动加载其 boss 的数据,Hibernate 会自动给 Person 类生成一个 Proxy,将其 boss 的 ID 付给这个 Proxy。只有在访问 boss 的具体数据的时候,这个 Proxy 才会自动调取数据。
一个有趣的现象。如果我们使用图形化调试器(比如 Eclipse)。当我们点开调试器中的 boss 的实例查看其数据的时候,这个 Proxy 就会去调取数据。这可能会使得调试和实际执行的行为不一致。
陷阱1:直接访问成员变量
Proxy 会将函数访问转到其父类的同样函数处理。但是 Proxy 自己是不会持有数据的,其自己的成员变量(比如 boss 的姓名)都是 null。 比如,一个 Person 的 equals 函数可能会这样写:
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
return name.equals((Person)obj).name);
}
上面最后那句话看起来没什么问题,也是常见写法。可是,当被传进来的 obj 可能是个 Proxy 的时候,直接访问 obj 的 name 成员变量就不对了 - 因为这永远是 null。因此,访问另一个 obj 的成员变量时要用 getter / setter。
陷阱2:instanceOf
如果用 instanceOf 判断一个对象是否是 Person 类,那么就没有考虑到 Proxy 的情况。文章给出的建议是关掉 lazy loading,恐怕不是个很好的建议。可以考虑用 Class.isAssignableFrom 吧。