Java注解处理器中如何检测package-info是否已存在?
Java注解处理器:检测package-info是否已存在的方案
我在编写Java注解处理器时,遇到了生成package-info文件的痛点:直接调用Filer.createSourceFile会在文件已存在时崩溃,但现有API无法直接检测package-info是否存在——ElementUtils.getPackageElement不管有没有package-info都会返回PackageElement,PackageElement.getEnclosedElements也只返回包内的类和接口,没法用来判断。下面是几种可行的检测方案:
利用Filer的getResource方法检查
Filer提供了资源查询接口,可以尝试从注解处理输出目录和项目源码路径中查找package-info.java,找不到则说明可以生成:
boolean packageInfoExists = false; String packagePath = packageName.replace('.', '/'); // 先检查注解处理输出目录是否已有生成的package-info try { processingEnv.getFiler().getResource(StandardLocation.SOURCE_OUTPUT, "", packagePath + "/package-info.java"); packageInfoExists = true; } catch (FileNotFoundException e) { // 输出目录没有,再检查项目源码目录是否已有原始文件 try { processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", packagePath + "/package-info.java"); packageInfoExists = true; } catch (FileNotFoundException ex) { // 两处都没有,确认可以生成 } } if (packageInfoExists) { return; // 跳过生成逻辑 } // 执行生成package-info的代码 JavaFileObject builderFile = filer.createSourceFile(packageName + ".package-info"); try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { out.println("... file content ..."); }
注意要将包名转换成路径格式(用/替换.),因为getResource的第三个参数是相对路径的文件名。
通过RoundEnvironment遍历根元素判断
在process方法中,遍历RoundEnvironment.getRootElements(),检查每个包元素是否有对应的源文件:
for (Element rootElement : roundEnv.getRootElements()) { if (rootElement.getKind() == ElementKind.PACKAGE) { PackageElement pkgElement = (PackageElement) rootElement; if (pkgElement.getQualifiedName().toString().equals(packageName)) { // 获取包元素的源文件位置,不为空则说明存在package-info if (processingEnv.getMessager().getPosition(pkgElement).getSourceFile() != null) { return; // 跳过生成 } } } }
不过这种方式依赖编译器实现细节,不同编译器(比如javac和ECJ)的行为可能有差异,兼容性不如第一种方法。
关于捕获IOException的说明
如果以上方案存在适配问题,直接捕获Filer.createSourceFile抛出的IOException其实是合规且常用的做法。Java注解处理器规范中,Filer创建已存在文件时抛出的IOException本身就明确表示文件重复,很多成熟处理器都用这种方式处理重复生成场景,无需额外提前检测。
内容的提问来源于stack exchange,提问作者Marten
相关产品推荐
相关产品推荐

