Search code examples
javacglib

Are Cglib Method Interceptors created per class instance?


I have the following generic cglib method interceptor implementation:

public class EntityInterceptor<T, PK> implements MethodInterceptor{

    private EntityChangeType changeType;
    private T entity;
    private GenericCrudMapper<T,PK> mapper;

    public EntityInterceptor(T entity, GenericCrudMapper<T, PK> mapper){
        this.entity = entity;
        this.mapper = mapper;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // pass through
        return proxy.invoke(entity, args);
    }

    public void setEntityChangeType(EntityChangeType changeType){
        this.changeType = changeType;
    }

    public void saveChanges(){
        // @todo
    }

}

Which is used as follows:

@Override
    public Airport get(String id) {
        Airport airport = airportMapper.findById(id);
        if(airport != null){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Airport.class);
            enhancer.setCallback(new EntityInterceptor<>(airport, airportMapper));
            airport = (Airport) enhancer.create();
            return airport;
        }
        return airport;
    }

Can I safely assume that the method interceptor created above will be instantiated per enhanced class instance (in other words, when assigning method interceptors, they are not shared between all class instances)?


Solution

  • Yes, you can be sure about this. Your solution is however fairly inefficient as a new class is generated for every instance. Instead, you should create your proxy based on the interceptor type:

    // Do once
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(realObject.getClass());
    enhancer.setCallbackType(Airport.class);
    Class classForProxy = enhancer.createClass();
    
    // Do for each instance
    Enhancer.registerCallbacks(classForProxy, new Callback[]{new EntityInterceptor<>(airport, airportMapper});
    Object createdProxy = classForProxy.newInstance();
    

    This way you can reuse a single class for all proxy instances.