在Hibernate代理存在的情况下,我需要在哪些地方避免直接字段访问



我有一个简单的实体类如下:

@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方法中。

最新更新