无需--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
相关产品推荐
相关产品推荐

