Reputation: 1371
I have a problem while reading annotations off methods of a proxied class.
There is an interface, an object and an annotation on a method, this part is really simple:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface A {
}
interface I {
void method();
}
class Test implements I {
@A
public void method() { }
}
Next, there is an InvocationHandler
that does nothing, just simply calls the method with the arguments passed:
class DefaultInvocationHandler implements InvocationHandler {
@Override
public Object invoke(final Object o, final Method method, final Object[] args) throws Throwable {
return method.invoke(o, args);
}
}
And there is a main
method that prints declared methods of a Test
instance and its proxied counterpart:
class Main {
public static void main(String[] args) {
Object test = new Test();
printMethods(test); // Outputs that `I#method` has `A` annotation
System.out.println();
Object proxied = Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), new DefaultInvocationHandler());
printMethods(proxied); // Outputs that `I#method` does not have `A` annotation
}
static void printMethods(Object obj) {
Arrays.stream(obj.getClass().getDeclaredMethods())
.forEach(method -> System.out.println(method.toString() + " has A annotation: " + method.isAnnotationPresent(A.class)));
}
}
And here comes the problem: local variable test
has is an instance of Test
class, and local variable proxied
is actually a Proxy
, so it does not have any annotations on its methods. Here's the output of the program:
public void Test.method() has A annotation: true // <- good thing
public final boolean $Proxy2.equals(java.lang.Object) has A annotation: false
public final java.lang.String $Proxy2.toString() has A annotation: false
public final void $Proxy2.method() has A annotation: false // <- bad thing
public final int $Proxy2.hashCode() has A annotation: false
I've tried searching for the solution, but this question is about extracting annotations off an annotation (I presume), this one is too about annotation class. Some of them are about other proxy implementations.
➥ So, is there any way to get actual annotations off a proxied object, or to expose the class that is hidden under the proxy (I want the former one, though)?
Upvotes: 4
Views: 3270
Reputation: 279990
So, is there any way to get actual annotations off a proxied object, or to expose the class that is hidden under the proxy (I want the former one, though)?
Not directly, no.
The idea behind Proxy
's design is that the actual wrapped instance (if there even is one) would be hidden behind the invocation handler. You can see this from the newProxyInstance
method: there's no reference to the test
instance passed anywhere. The Proxy
instance has no knowledge of your Test
instance.
A common pattern is to use a common InvocationHandler
subclass that keeps a reference to a wrapped instance and can return it to you and you can use that to perform your checks. For example,
abstract class InvocationHandlerWithTarget implements InvocationHandler {
protected final Object target;
public InvocationHandlerWithTarget(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
}
class DefaultInvocationHandler extends InvocationHandlerWithTarget {
public DefaultInvocationHandler(Object target) {
super(target);
}
@Override
public Object invoke(final Object o, final Method method, final Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
and then check if you're working with a Proxy
and whether its InvocationHandler
is what you expect
Object proxied = Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(),
new DefaultInvocationHandler(test));
[...]
if (Proxy.isProxyClass(proxied.getClass())) {
var handler = Proxy.getInvocationHandler(proxied);
if (handler instanceof InvocationHandlerWithTarget) {
var handlerWithTarget = (InvocationHandlerWithTarget) handler;
// now process the target
handlerWithTarget.getTarget();
}
}
You then have a concrete instance with which to reflect on (or whatever other processing you need to do).
Upvotes: 3