Objective: (as the question title says): to efficiently create proxy (domain) objects using CGLib or any other reflection library (e.g. ByetBuddy?)
Having our domain class (please note Lombok annotations):
@Getter
@Setter
@RequiredArgsConstructor
public class DomainFoo {
@NonNull
private final Integer id;
// some other fields, final or otherwise!
public void doSomething() {
// do something here!
}
}
I'm trying to create a proxy object of type DomainFoo
which responds only to getId
method with the given ID (see below), otherwise (calling any other method) it throws an UnsupportedOperationException
.
I managed to do this using GCLib (Spring version if it matters):
public static final ObjenesisStd OBJENESIS = new ObjenesisStd();
public static Factory newFoo(Integer id) {
val enhancer = new Enhancer();
enhancer.setSuperclass(domainClass);
enhancer.setCallbackType(MethodInterceptor.class);
val proxyClass = enhancer.createClass();
// Since there's no default constructor in domains:
val instantiator = OBJENESIS.getInstantiatorOf(proxyClass);
val proxyInstance = instantiator.newInstance();
val factory = (Factory) proxyInstance;
factory.setCallbacks(new Callback[]{new DomainProxyInterceptor(id)});
return factory; // see below for why it's called factory!
}
where DomainProxyInterceptor
is:
@RequiredArgsConstructor
private class DomainProxyInterceptor implements MethodInterceptor {
@NonNull
private final Integer id;
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass() != Object.class && method.getName().equals("getId")) {
return id;
} else {
throw new NotSupportedException(method.getName());
}
}
}
Everything works just fine. Now to make it efficient, I'm trying to cache factory
and actually use it a factory:
// newFooFactory is the above method rename!
private static final Factor FOO_FACTORY = newFooFactory(0);
public static DomainFoo newFoo(Integer id) {
val callbacks = new Callback[]{new DomainProxyInterceptor(id)};
return (DomainFoo) FOO_FACTORY.newInstance(callbacks);
}
But calling the above method throws a NoSuchMethodError
:
Exception in thread "main" java.lang.NoSuchMethodError: com/example/demo/DomainFoo$$EnhancerByCGLIB$$8423c3c4.<init>()V (loaded from file:/Users/rad/works/demo/target/classes/ by jdk.internal.loader.ClassLoaders$AppClassLoader@85a856f6) called from class com.example.demo.DomainFoo$$EnhancerByCGLIB$$8423c3c4 (loaded from file:/Users/rad/works/demo/target/classes/ by jdk.internal.loader.ClassLoaders$AppClassLoader@85a856f6).
at com.example.demo.DomainFoo$$EnhancerByCGLIB$$8423c3c4.newInstance(<generated>)
at com.example.demo.DomainFactory.proxyFoo(DomainFactory.java:28)
at com.example.demo.DemoApplication.main(DemoApplication.java:6)
Which presumably is because Factory.newInstance
tries to use the normal constructor but it's not there (because we created the object using Objenesis
?). I also tried ReflectionFactory
to create the factory instance, but it has the same issue.
Questions (they're kinda related):
- Do I need to worry about efficiency at all? (Those objects are normal domains, so there's usually too many of them.)
- Is there a better way to do the above? In terms of efficiency or otherwise? Specifically what's the right way to instantiate using GCLib when there's no default constructor?
- Is there a way to fix the above issue? Resolving
NoSuchMethodError
failure? - Any other library or solution I could try?
Thanks.
Aucun commentaire:
Enregistrer un commentaire