Anatomy of a Hibernate Proxy
Hibernate ORM is well known for its use of proxy classes to enable lazy loading.
A Hibernate proxy class
- extends the entity class
- implements org.hibernate.proxy.HibernateProxy
- implements org.hibernate.proxy.ProxyConfiguration
- has a field of type org.hibernate.proxy.ProxyConfiguration.Interceptor
- has a
static final
field of typejava.lang.reflect.Method
for every superclass method - routes all methods to org.hibernate.proxy.ProxyConfiguration.InterceptorDispatcher#intercept which then dispatches to org.hibernate.proxy.ProxyConfiguration.Interceptor#intercept
the generated proxy class looks like this
public class EntityClass$HibernateProxy$iPrgCr9u extends EntityClass implements HibernateProxy, ProxyConfiguration {
private ProxyConfiguration.Interceptor $$_hibernate_interceptor;
private static final Method cachedValue$xVIlXpzP$4cscpe1 = Object.class.getMethod("toString");
private static final Method cachedValue$xVIlXpzP$uoilpf3 = ChildEntity.class.getMethod("setId", Long.class);
private static final Method cachedValue$xVIlXpzP$o23rrk2 = HibernateProxy.class.getMethod("getHibernateLazyInitializer");
private static final Method cachedValue$xVIlXpzP$5j4bem0 = Object.class.getMethod("equals", Object.class);
private static final Method cachedValue$xVIlXpzP$gpia792 = HibernateProxy.class.getMethod("writeReplace");
private static final Method cachedValue$xVIlXpzP$7m9oaq0 = Object.class.getDeclaredMethod("clone");
private static final Method cachedValue$xVIlXpzP$9pqdof1 = Object.class.getMethod("hashCode");
private static final Method cachedValue$xVIlXpzP$8j4rtp1 = ChildEntity.class.getMethod("getId");
@Override
public boolean equals(Object obj) {
return (Boolean)InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$5j4bem0, new Object[]{obj}, false, this.$$_hibernate_interceptor);
}
@Override
public String toString() {
return (String)InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$4cscpe1, new Object[0], null, this.$$_hibernate_interceptor);
}
@Override
public int hashCode() {
return (Integer)InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$9pqdof1, new Object[0], 0, this.$$_hibernate_interceptor);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$7m9oaq0, new Object[0], null, this.$$_hibernate_interceptor);
}
@Override
public void setId(Long id) {
InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$uoilpf3, new Object[]{id}, null, this.$$_hibernate_interceptor);
}
@Override
public Long getId() {
return (Long) InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$8j4rtp1, new Object[0], null, this.$$_hibernate_interceptor);
}
@Override
public Object writeReplace() {
return InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$gpia792, new Object[0], null, this.$$_hibernate_interceptor);
}
@Override
public LazyInitializer getHibernateLazyInitializer() {
return (LazyInitializer) InterceptorDispatcher.intercept(this, cachedValue$xVIlXpzP$o23rrk2, new Object[0], null, this.$$_hibernate_interceptor);
}
@Override
public void $$_hibernate_set_interceptor(ProxyConfiguration.Interceptor interceptor) {
this.$$_hibernate_interceptor = interceptor;
}
}
A few things are important to note here:
- All methods that can be intercepted are intercepted
- Proxies can be cast to
HibernateProxy
orProxyConfiguration
and their methods can be invoked by anybody - Every object can have its own
Interceptor
If you want to have a look at Hibernate proxy classes for yourself give Hibernate Proxy Dumper a try.