You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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()方法上,就能被正常识别并打印。


问题原因

  1. JDK动态代理的本质:JDK动态代理是基于接口实现的,生成的代理类只会实现目标接口的方法,因此invoke方法中传入的method参数对应的是接口中的方法对象,而非实现类中的方法对象。
  2. 方法注解的继承限制:即使注解添加了@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 18:11:08