【问题标题】:Java Annotation processor for remote JAR用于远程 JAR 的 Java 注释处理器
【发布时间】:2015-08-12 14:18:59
【问题描述】:

一般问题

我有两个项目 A 和 B; B 依赖于 A。我想根据 A 中对象的注释在 B 中使用注释处理器生成一些代码。当我使用正确的处理器实现运行编译时,只拾取来自 B 的注释对象。

我知道默认情况下必须禁用扫描其他 JAR,因为您通常不希望对所有依赖项进行注释扫描。我也明白,由于编译器的魔力,我可能无法做我想做的事——我不太了解——但我希望不是这样。

具体情况

我的项目称为 DB 和 WEB。 WEB 显然依赖于 DB 的 JPA 访问;这是在 Maven 中配置的。由于许多架构选择,DB 必须保持一个单独的 JAR。 DB 不使用 Spring,除了一些被 WEB 消费的注解; WEB 使用 Spring MVC。

我正在尝试使用注释处理器为我的所有 JPA 实体生成 CrudRepository 接口。 @Repository 对象应该放在 WEB 项目的 repo 包中,因此它们可以在我的 WEB 应用程序中的任何位置与 @Autowired 一起使用。我正在执行扫描的注释是@javax.persistence.Entity,但我也尝试了自定义注释,结果相同。

@SupportedAnnotationTypes("javax.persistence.Entity")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RepositoryFactory extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(Entity.class)) {
            if (e.getKind() != ElementKind.CLASS) {
                continue;
            }
            // TODO: implement logic to skip manually implemented Repos
            try {
                String name = e.getSimpleName().toString();
                TypeElement clazz = (TypeElement) e;

                JavaFileObject f = processingEnv.getFiler().
                        createSourceFile("blagae.web.repo." + name + "Repo");
                try (Writer w = f.openWriter()) {
                    PrintWriter pw = new PrintWriter(w);
                    pw.println("package blagae.web.repo;");
                    pw.println("import org.springframework.data.repository.CrudRepository;");
                    pw.printf("import %s;\n", clazz.toString());
                    pw.println("import org.springframework.stereotype.Repository;");
                    pw.println("@Repository");
                    pw.printf("public interface %sRepo extends CrudRepository<%s, Long> {}\n", name, name);
                    pw.flush();
                }
            } catch (IOException ex) {
                Logger.getLogger(RepositoryFactory.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return false;
    }
}

理想情况下,我希望有人能告诉我一个简单的注释

@ComponentScan(basePackages = "blagae.db.*")

但是,我当然不会指望它,因为它可能会记录在某个地方。作为一种解决方法,我可以将 Spring 依赖项添加到 db 并在那里生成类,但它们仅在 Spring MVC 应用程序中起作用。我也对完成这项工作可能需要的配置保持警惕。

更新

一些额外的信息:我正在使用 maven-processor-plugin,我已经验证它在 WEB 项目中对于在那里定义的类运行良好。但是,我特别想要在依赖项目数据库中注释的访问类。我已经研究了AbstractProcessor::getSupportedOptions 的方法,但我不清楚我可以在那里做什么。

Maven 配置:

<plugin>
    <groupId>org.bsc.maven</groupId>
    <artifactId>maven-processor-plugin</artifactId>
    <version>2.2.4</version>
    <configuration>
        <processors>
            <processor>blagae.utils.RepositoryFactory</processor>
        </processors>
    </configuration>
    <executions>
        <execution>
            <id>process</id>
            <goals>
                <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
        </execution>
    </executions>
</plugin>

建议

我的另一个随机想法是在 WEB 中为 DB 项目运行 JavaCompiler 进程,但我将如何注入我的 Processor

【问题讨论】:

  • 我不明白这个问题 - 注释处理器不工作还是......? :)
  • 我希望在 WEB 项目上运行注释处理器时从 DB 项目 (= jar) 中提取注释。到目前为止,这还没有发生。
  • 对。我有一个可以建议的解决方案。稍等片刻,我就可以写下来了。 :)
  • 再问一个问题。你在用 Eclipse 吗?
  • 我实际上在这个项目中使用 Netbeans,但我总是愿意改变

标签: java maven jpa annotation-processing javacompiler


【解决方案1】:

就个人而言,我会将注释处理器提取到一个单独的 maven 模块中,并从 WEB 模块中添加一个依赖项。

但是,这对于成功触发注释处理器并不重要。

为了使注释处理器正常工作,您必须提供两件事:

由于您提到当前没有生成类,我假设您缺少元文件。因此,打开您的 WEB 项目并导航到 src/main/resouces 文件夹。在其中,您必须创建一个 META-INF 文件夹,其中包含一个嵌套的 services 文件夹。然后,在services 中创建一个名为javax.annotation.processing.Processor 的文件。文件的内容应该列出注释处理器的完全限定类名。如果有多个注解处理器,则完全限定的类名应该在不同的行上。但既然你只有一个,你就会有类似的东西:

com.yourdomain.processor.RepositoryFactory

请注意,您必须使用注释处理器的实际完全限定类名更改此行。

最后,你应该得到类似的结构:

这个元文件很重要,否则编译器不知道用户定义的注释处理器。拥有它,它将利用所有已注册的处理器。

之后,当您执行mvn clean install 时,您的所有模块都将被清理和构建。然而,由于编译器现在会知道你的注解处理器,它会触发它。所有生成的源将位于(默认情况下)target/generated-sources 文件夹中。此外,它们都将在您在注释过程中配置的包下,即blagae.web.repo

为了在您的代码中使用生成的源代码,您必须将targer/generated-sources 添加到项目类路径中。如果您不想依赖 IDE 来执行此操作,可以通过将 target/generated-sources 添加到类路径来扩展 maven &lt;build&gt;。比如:

<build>
    <resources>
        ...
        <resource>
            <directory>${project.build.directory}/generated-resources</directory>
        </resource>
    </resources>
</build>

【讨论】:

  • 很抱歉,这不起作用。问题仍然是预编译器运行只选择当前项目中带注释的类/方法(即尚未编译的类)。
  • 我已经在使用 maven-processor-plugin 并且它确实拾取了在虚拟运行期间在 WEB 本身中使用的其他注释(例如 @Controller)。您的建议与 maven-processor-plugin 做同样的事情,即适用于当前项目,但不依赖于依赖项
【解决方案2】:

注释处理器在您的项目(在您的情况下为 WEB)的编译阶段工作,编译器编译该项目。当前项目的依赖项已经编译,编译器(以及您的注释处理器)不会接触(或无法访问)第三方库 (DB)。

您可以尝试将注解处理器提取到单独的项目/jar 中,并在 WEB 和 DB 项目中使用它。在这种情况下,注释处理器将在具体项目的编译阶段创建CrudRepository。 DB项目中生成的所有类都将在WEB中可用。

【讨论】:

  • 注解处理器没有触及第三方库是对的。这就是我的具体问题:如果不是不可能的话,如何强制访问依赖项...
  • @blagae,是的,这是不可能的,因为注释处理器仅适用于源文件(.java,但不适用于编译文件 - .class)。在其他情况下,java 编译器应该反编译已经编译的代码来处理它——这是荒谬的。
  • 请查看我原来的问题的更新,特别是关于使用JavaCompiler
  • @blagae javac /path/to/sources \ -cp /path/to/classpath \ -processorpath /path/to/annotation-processor.jar
【解决方案3】:

在您的项目 A 中包含 META-INF/beans.xml 文件, 其中将包含以下内容:

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee <a href="http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"</a>
       version="1.1" bean-discovery-mode="all">
</beans>

并试一试。您应该使用 JavaEE 7/CDI 1.1。请参阅Java EE 7 Deployment Descriptors 了解更多信息。

也可以参考这个相关问题:How to @Inject object from different project module included as jar

【讨论】:

    猜你喜欢
    • 2017-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多