如何使用注解处理器从src/main/resources中读取文件?

13

我有一个简单的注解处理器,需要从与被注释类相同的项目中读取配置文件。例如结构:

- myproject 
  - src
    - main
      - java
        - my.package.SourceFile
      - resources
        - config.json

在注解处理器中,我尝试读取文件:

FileObject resource = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", "config.json");

但是它会抛出FileNotFoundException。我还尝试了其他路径,比如../resources/config.json(它会抛出Invalid relative name: ../resources/config.json),以及将配置文件放在src/main/java(甚至是src/main/java/my/package)中,虽然我不喜欢这样做,但仍然会抛出FileNotFoundException

如果能让filer.getResource()告诉我它在哪里查找,已经很有帮助了。为了找出答案,我尝试生成一个文件:

filer.createResource(StandardLocation.SOURCE_OUTPUT, "", "dummy");

这个错误产生在myproject/build/classes/main/dummy目录下。不幸的是,我不能在SOURCE_PATH中生成它,所以这并没有帮助我找到问题。


我不知道你正在使用的API,但如果它将源路径作为基础,那么解析为main/java/my/package/config.json,所以用..向上一级可能不够。你可以尝试将资源放在与源文件相同的包中,即main/resources/my/package/config.json - Jorn Vernee
我不确定为什么源路径会以类的位置作为基础。在读取配置文件时,没有任何关于类位置的引用。它并不是这样的。我原本期望它会采用src/main/java,但显然它并不是这样做的。 我尝试将资源放在与源文件相同的位置,但也没有找到。 - Jorn
好的,我认为它可能将SOURCE_PATH作为当前正在处理的源文件的路径。 - Jorn Vernee
我找到了文档,其中提到“相对名称是由路径段组成的非空序列,路径段之间用'/'分隔;'.'和'..'是无效的路径段”。这就解释了为什么使用..是无效的。 - Jorn Vernee
我知道 - 我只是试过使用相对路径来解决问题。问题是,为什么资源文件夹中的内容不在源路径上?而且,为什么位于源路径上的文件仍然无法找到? - Jorn
路径应该是“/config.json”,但资源必须在注解处理器的类路径上。因此,首先必须编译类(资源位于目标类目录下),并且处理器的类路径必须正确,然后才能进行处理。那些带注解的类需要config.json吗? - Joop Eggen
5个回答

7

我希望在构建过程中(在注解处理之前),src/main/resources中的内容被复制到target/classes目录下。如果是这样,你可以按照以下方式打开它们:

ProcessingEnvironment pe = ...;
FileObject fileObject = pe.getFiler()
    .getResource( StandardLocation.CLASS_OUTPUT, "", "config.json" );
InputStream jsonStream = fileObject.openInputStream();

1
在 Gradle 构建过程中,似乎没有将资源复制到输出类文件夹中。 - Michael Conrad

4

我已经和Project Lombok的开发人员之一讨论了这个问题。如果有人了解注解处理,那就是他们了;)

我们的结论是,内部处理请求的 JavacFileManager 没有解析 StandardLocation.SOURCE_PATH 的路径。我们不确定,但可能与使用Gradle构建有关。


1

我曾经也遇到过同样的问题,搜索了一段时间后发现了这个很酷的技巧,可以解决Android的问题。

下面是我从纯Java/Kotlin项目中找到的解决方案。

fun ProcessingEnvironment.getResourcesDirectory(): File {
val dummySourceFile = filer.createSourceFile("dummy" + System.currentTimeMillis())
var dummySourceFilePath = dummySourceFile.toUri().toString()

if (dummySourceFilePath.startsWith("file:")) {
    if (!dummySourceFilePath.startsWith("file://")) {
        dummySourceFilePath = "file://" + dummySourceFilePath.substring("file:".length)
    }
} else {
    dummySourceFilePath = "file://$dummySourceFilePath"
}

val cleanURI = URI(dummySourceFilePath)

val dummyFile = File(cleanURI)

val projectRoot = dummyFile.parentFile.parentFile.parentFile.parentFile.parentFile

return File(projectRoot.absolutePath + "/resources")
}

使用 .parentFile.parentFile ... 需要特定的包深度 - 对于使用不同数量 "." 的项目,将无法工作。 - Michael Conrad

0
以下函数适用于我的注解处理器被Gradle触发时,它并不是最美观的,但能够工作:
private fun resolveApplicationPropertiesFile(): File {
    val projectRoot = Path.of(processingEnv.filer.getResource(StandardLocation.CLASS_OUTPUT, "", "doesntmatter")
            .toUri())
            .parent
            .parent
            .parent
            .parent
            .parent
            .parent
    val properties = Path.of(projectRoot.toString(), "src", "main", "resources", "application.yml")
    return properties.toFile()
}

processingEnvAbstractProcessor 的成员之一。


-2
  • 如果您的元素是TypeElement的实例,则可以使用以下代码查找源代码:

    FileObject fileObject = processingEnv.getFiler().getResource( StandardLocation.SOURCE_PATH, element.getEnclosingElement().toString(), element.getSimpleName() + ".java");

    1. element.getEnclosingElement()是您的类包,例如:com.fool
    2. element.getSimpleName()是您的类名,例如:Person
  • 然后您可以打印它们:

    CharSequence content = fileObject.getCharContent(true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接