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

无需--add-opens使用com.sun类:shadowJar类型转换问题求解

问题分析与解决方案

为什么ShadowJar重定位会失败

你尝试用shadowJar重定位com.sun.tools.javac包到net.phoenix.javac的思路本质上不可行:注解处理器运行在JDK自带的javac编译进程中,你拿到的ProcessingEnvironment实例是JDK启动类加载器加载的原生com.sun.tools.javac.processing.JavacProcessingEnvironment,而代码里的net.phoenix.javac.processing.JavacProcessingEnvironment是应用类加载器加载的重定位版本——两个类属于不同类加载上下文,类型转换必然失败。


可行解决方案

方案一:用Gradle插件自动注入JVM参数(最直接的兼容方案)

无需修改处理器核心代码,开发一个轻量Gradle插件,在用户引入你的库时自动为编译任务添加--add-exports参数,彻底免除用户手动配置的麻烦。

插件核心代码(简化版):

class DependencyInjectorPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.tasks.withType(JavaCompile) { task ->
            task.options.fork = true
            task.options.forkOptions.jvmArgs.addAll([
                '--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED',
                '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
                '--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED'
            ])
        }
    }
}

用户使用方式:
用户只需在build.gradle中添加插件和依赖即可,无需手动写编译配置:

plugins {
    id 'net.phoenix.dependency-injector' version 'x.y.z'
}

dependencies {
    annotationProcessor 'net.phoenix:dependency-injector:x.y.z'
}

方案二:通过反射访问javac内部API(避免强转依赖)

修改注解处理器代码,不直接强转ProcessingEnvironment为JavacProcessingEnvironment,而是通过反射获取内部对象,避免编译时直接依赖com.sun包,同时配合方案一的插件自动注入权限参数。

对应你代码第41行的修改示例:

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    ProcessingEnvironment env = roundEnv.getProcessingEnvironment();
    try {
        // 反射获取JavacProcessingEnvironment的context对象
        Method getContextMethod = env.getClass().getMethod("getContext");
        Object context = getContextMethod.invoke(env);
        
        // 反射获取TreeMaker、Names等核心工具类
        Class<?> contextClass = Class.forName("com.sun.tools.javac.util.Context");
        Method getMethod = contextClass.getMethod("get", Class.class);
        
        Class<?> treeMakerClass = Class.forName("com.sun.tools.javac.tree.TreeMaker");
        Object treeMaker = getMethod.invoke(context, treeMakerClass);
        
        // 后续AST修改操作均通过反射完成
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
        // 处理反射异常
        e.printStackTrace();
    }
    return true;
}

最优组合

实际开发中推荐方案一+方案二结合:用Gradle插件自动为用户处理权限配置,处理器内部用反射访问javac API,既解决了用户配置繁琐的问题,也彻底规避了类加载导致的类型转换错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 01:03:18