我有一个简单的实体类如下:
@Entity public class Foo {
@NotNull @Column private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
现在面对Hibernate代理,我知道我不能依赖getClass()
来返回Foo.class
,也不能依赖instanceof
来面对实体层次结构。我知道我不应该在equals
方法中直接访问other.name
。例如,我不应该这样做:
// in class Foo:
@Override public boolean equals(Object obj) {
// instanceof is fine in this case since Foo has no entity superclass
if (!(obj instanceof Foo)) {
return false;
}
Foo other = (Foo) obj;
// hold up! if other is a proxy, then other.name will be null
return Objects.equal(name, other.name);
}
我的问题是,到底访问other.name
是不安全的吗?访问this.name
是否不安全?
这将很快投入生产,我没有"试一试"的奢侈方法。
为了回答这个问题,我设置了Foo
类来报告不同上下文中的字段访问。以下是我添加的内容:
public String callGetNamePublic() {
System.out.print("callGetNamePublic: ");
return getNamePublic();
}
public String getNamePublic() {
System.out.println("getNamePublic: " + name);
return name;
}
public String callGetNameProtected() {
System.out.print("callGetNameProtected: ");
return getNameProtected();
}
protected String getNameProtected() {
System.out.println("getNameProtected: " + name);
return name;
}
public String callGetNamePackage() {
System.out.print("callGetNamePackage: ");
return getNamePackage();
}
String getNamePackage() {
System.out.println("getNamePackage: " + name);
return name;
}
public String callGetNamePrivate() {
System.out.print("callGetNamePrivate: ");
return getNamePrivate();
}
private String getNamePrivate() {
System.out.println("getNamePrivate: " + name);
return name;
}
public static class FooSubclass extends Foo {
public String callOtherGetNamePublic(Foo other) {
System.out.print("callOtherGetNamePublic: ");
return other.getNamePublic();
}
public String callOtherGetNameProtected(Foo other) {
System.out.print("callOtherGetNameProtected: ");
return other.getNameProtected();
}
public String callOtherGetNamePackage(Foo other) {
System.out.print("callOtherGetNamePackage: ");
return other.getNamePackage();
}
public String callOtherGetNamePrivate(Foo other) {
System.out.print("callOtherGetNamePrivate: ");
return other.getNamePrivate();
}
public String accessOtherName(Foo other) {
System.out.print("accessOtherName: " + other.name);
return other.name;
}
}
然后我设置了一个测试来拉出一个Foo
代理对象,并调用这些方法。像这样:
// I'll leave this to your imagination ;)
Foo foo1 = getFooProxy();
foo1.getName();
foo1.getNamePublic();
foo1.callGetNamePublic();
foo1.callGetNameProtected();
foo1.callGetNamePackage();
foo1.callGetNamePrivate();
FooSubclass foo2 = new Foo.FooSubclass();
foo2.callOtherGetNamePublic(foo1);
foo2.callOtherGetNameProtected(foo1);
foo2.callOtherGetNamePackage(foo1);
foo2.callOtherGetNamePrivate(foo1);
foo2.accessOtherName(foo1);
结果如下:
getNamePublic: testName
callGetNamePublic: getNamePublic: testName
callGetNameProtected: getNameProtected: testName
callGetNamePackage: getNamePackage: testName
callGetNamePrivate: getNamePrivate: testName
callOtherGetNamePublic: getNamePublic: testName
callOtherGetNameProtected: getNameProtected: testName
callOtherGetNamePackage: getNamePackage: testName
callOtherGetNamePrivate: getNamePrivate: null
accessOtherName: null
我已经发现在任何final方法中都需要避免直接的字段访问。如果您将以下任何方法更改为final,则相应的结果将从testName
更改为null
:
-
getNamePublic
-
getNameProtected
-
getNamePackage
-
getNamePrivate
所以答案是:面对Hibernate代理,直接字段访问的唯一限制是(1)对other.name
的直接字段访问,(2)在other
上对私有方法的内部调用,以及(3)在任何final方法中。