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

如何解决从无名模块创建MethodHandle时的IllegalAccessError

JDK 21中获取TypeMetadata.Annotations构造方法MethodHandle的权限问题

我在JDK 21中尝试获取com.sun.tools.javac.code.TypeMetadata.Annotations记录的无参构造方法的MethodHandle,代码如下:

import com.sun.tools.javac.code.TypeMetadata;
import java.lang.invoke.*;
import java.util.*;

class TestMH {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodType mt = MethodType.methodType(void.class);
    MethodHandle mh = lookup.findConstructor(TypeMetadata.Annotations.class, mt);
    System.out.println(mh);
  }
}

使用以下命令编译:

javac --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED TestMH.java

运行命令:

java --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED TestMH

执行后出现报错:

Exception in thread "main" java.lang.IllegalAccessException: no such constructor: com.sun.tools.javac.code.TypeMetadata$Annotations.<init>()void/newInvokeSpecial
    at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:911)
    at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:994)
    at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3750)
    at java.base/java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:2837)
    at TestMH.main(TestMH.java:9)
Caused by: java.lang.IllegalAccessError: class TestMH tried to access method 'void com.sun.tools.javac.code.TypeMetadata$Annotations.<init>()' (TestMH is in unnamed module of loader 'app'; com.sun.tools.javac.code.TypeMetadata$Annotations is in module jdk.compiler of loader 'app')
    at java.base/java.lang.invoke.MethodHandleNatives.resolve(Native Method)
    at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:962)
    at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:991)
    ... 3 more

请问除了--add-opens参数外,是否有其他方法绕过该IllegalAccessError?还是这是模块机制的根本限制?


核心问题与解决方案

首先纠正一个基础错误:TypeMetadata.Annotations是Record类,它默认不会生成无参构造方法——Record类的规范构造器(canonical constructor)参数列表与它的组件完全匹配(这里组件是List<? extends Annotation>),你代码中指定的void.class对应的无参构造根本不存在,这是报错的直接原因之一。

即便你使用正确的构造方法签名,模块权限限制仍然存在。以下是无需额外--add-opens参数的合法解决方案:

方案1:通过MethodHandles.privateLookupIn合法获取构造方法的MethodHandle

JDK 9+提供的privateLookupIn API允许跨模块获取私有成员的访问权限,只要你能拿到目标类的引用(这一步通过--add-exports已经满足)。修改代码如下:

import com.sun.tools.javac.code.TypeMetadata;
import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.util.List;

class TestMH {
  public static void main(String[] args) throws Throwable {
    // 获取Annotations的规范构造方法(参数为List<? extends Annotation>)
    Constructor<TypeMetadata.Annotations> ctor = 
        TypeMetadata.Annotations.class.getDeclaredConstructor(List.class);
    
    // 使用privateLookupIn获取跨模块的Lookup实例
    MethodHandles.Lookup lookup = 
        MethodHandles.privateLookupIn(TypeMetadata.Annotations.class, MethodHandles.lookup());
    
    // 将反射构造器转换为MethodHandle
    MethodHandle mh = lookup.unreflectConstructor(ctor);
    
    // 调用构造方法生成实例(传入空列表测试)
    TypeMetadata.Annotations instance = (TypeMetadata.Annotations) mh.invoke(List.of());
    System.out.println(instance);
  }
}

编译命令保持不变:

javac --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED TestMH.java

运行时无需添加--add-opens,因为privateLookupIn是模块系统允许的合法跨模块访问方式。

方案2:避免直接依赖内部API

com.sun.tools.javac下的类属于JDK内部API,没有向后兼容性保证。如果你的需求是处理编译元数据,建议使用官方标准的javax.tools API(比如JavaCompiler、JavaFileObject等),通过合法入口获取元数据,而非直接调用内部类的构造方法。

关于模块机制的限制

模块机制的核心设计就是封装内部API,禁止外部代码随意访问非导出的私有/包私有成员。如果不使用--add-opens(强制开放包)或privateLookupIn(合法授权访问),无法绕过这个限制——这是模块系统的根本约束,目的是提高JDK的稳定性和安全性。

内容的提问来源于stack exchange,提问作者msridhar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 15:27:02