为何InvocationHandler无法识别实现类的Runtime方法注解及解决方法
问题分析与解决方案
相关代码示例
MyAnnotation类
package test; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { }
MyInterface接口
package test; public interface MyInterface { void doSomething(); }
MyClass实现类
package test; import java.lang.reflect.Proxy; public class MyClass implements MyInterface { @Override @MyAnnotation public void doSomething() { System.out.println("Do something from MyClass"); } public static void main(String[] args) { var obj = new MyClass(); var proxy = (MyInterface)Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MyInvocationHandler(obj)); proxy.doSomething(); } }
MyInvocationHandler调用处理器
package test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class MyInvocationHandler implements InvocationHandler { private final MyClass origin; public MyInvocationHandler(MyClass origin) { this.origin = origin; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(Arrays.toString(method.getAnnotations())); method.invoke(origin, args); return proxy; } }
现象说明
运行上述代码时,MyInvocationHandler的invoke方法中执行System.out.println(Arrays.toString(method.getAnnotations()))会输出空数组,但明明已经在MyClass.doSomething()方法上标注了@MyAnnotation注解。而如果将注解移到MyInterface.doSomething()方法上,就能被正常识别并打印。
问题原因
- JDK动态代理的本质:JDK动态代理是基于接口实现的,生成的代理类只会实现目标接口的方法,因此
invoke方法中传入的method参数对应的是接口中的方法对象,而非实现类中的方法对象。 - 方法注解的继承限制:即使注解添加了
@Inherited元注解,它也只对类级别的注解生效,无法继承方法上的注解。所以接口方法的注解不会自动传递给实现类方法,反过来实现类方法的注解也不会被接口方法对象获取到。
解决方案
要在InvocationHandler中获取实现类方法上的注解,需要通过原始对象的Class对象,根据接口方法的签名(方法名+参数类型)找到对应的实现类方法,再获取其注解。修改后的MyInvocationHandler代码如下:
package test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class MyInvocationHandler implements InvocationHandler { private final MyClass origin; public MyInvocationHandler(MyClass origin) { this.origin = origin; } @Override public Object invoke(Object proxy, Method interfaceMethod, Object[] args) throws Throwable { // 获取实现类中对应的方法 Method implMethod = origin.getClass().getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes()); // 打印实现类方法的注解 System.out.println(Arrays.toString(implMethod.getAnnotations())); interfaceMethod.invoke(origin, args); return proxy; } }
补充说明
- 如果存在方法重载的情况,需要更严谨地匹配方法(比如结合返回值类型,但Java中方法签名不包含返回值,所以如果有同方法名同参数不同返回值的情况,需要额外处理)。
- 如果原始对象可能是子类,还可以考虑使用
getDeclaredMethod来获取当前类声明的方法,避免父类方法的干扰。
内容的提问来源于stack exchange,提问作者Georgi Georgiev
相关产品推荐
相关产品推荐

