用于远程JAR的Java Annotation处理器

bla*_*gae 11 java jpa maven annotation-processing javacompiler

一般问题

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

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

具体情况

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

我正在尝试CrudRepository使用Annotation Processor为我的所有JPA实体生成接口.这些@Repository对象应该repo放在WEB项目的包中,因此它们可以@Autowired在我的WEB应用程序中的任何地方使用.我正在执行扫描的注释是@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

理想情况下,我喜欢有人告诉我一个简单的注释

@ComponentScan(basePackages = "blagae.db.*")
Run Code Online (Sandbox Code Playgroud)

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

UPDATE

一些额外的信息:我正在使用maven-processor-plugin,我已经验证它在WEB项目中适用于那里定义的类.但是,我特别希望在依赖项目DB中注释访问类.我已经研究了这个方法,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>
Run Code Online (Sandbox Code Playgroud)

建议

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

小智 6

注释处理器适用于项目的编译阶段(在您的情况下为WEB),编译器编译此项目.已编译当前项目的依赖关系,并且编译器(以及您的注释处理器)不会触及(或无法访问)第三方库(DB).

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


Kon*_*kov 5

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

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

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

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

com.yourdomain.processor.RepositoryFactory
Run Code Online (Sandbox Code Playgroud)

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

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

在此处输入图片说明

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

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

为了在您的代码中使用生成的源代码,您必须将 加入targer/generated-sources到项目类路径中。如果您不想依赖 IDE 来执行此操作,您可以<build>通过向target/generated-sources类路径添加来扩展 Maven 。就像是:

<build>
    <resources>
        ...
        <resource>
            <directory>${project.build.directory}/generated-resources</directory>
        </resource>
    </resources>
</build>
Run Code Online (Sandbox Code Playgroud)