How should the lazy loading behaviour be enabled and what should be the default?
I would propose something like this, with lazy=false as the default:
@DBRef(lazy = true) List<User> fans;
Should we support lazy loading for all possible concrete types or just interfaces types or even just for collection types?
If the raw type has to be an interface we could simply return a JDK Proxy with an appropriate InvocationHandler that checks an "initialised"-flag on every method call. If not yet initialised the proxy will resolve the DBRef, store the result in a field and return that on subsequent invocations.
If we have to support arbitrary types, then we have to enforce some restrictions, e.g. like the fields of the type needn't be accessed directly - just via accessor methods. We could then generate a custom subclass of the given type (via CGLIB / ASM + (maybe) Objenesis for object construction) where we inject a proper initialisation check into the relevant method bodies and perform the required lazy loading logic if necessary and then delegate to the base class implementation.