远程JAR的Java注解处理器

11

一般问题

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

我理解默认情况下必须禁用对其他JAR的扫描,因为通常不希望扫描所有依赖项的注释。我也明白由于编译器魔法(我对此知之甚少)可能无法做到我想做的事情,但我希望不能这样。

具体案例

我的项目名为DB和WEB。 WEB显然依赖于DB以获取其JPA访问权限;这在Maven中进行了配置。由于一些架构选择,DB必须保持单独的JAR。除了WEB使用Spring MVC外,DB并不使用Spring,但会使用一些注解,这些注解被WEB所消耗。

我正在尝试使用注释处理器为所有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项目中定义的类中,我已经验证它的工作情况良好。然而,我特别想访问在依赖项目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>

建议

我又想到了一个随机的想法,就是在 WEB 的 DB 项目中运行一个 JavaCompiler 进程,但是我该如何注入我的 Processor 呢?


我不明白这个问题 - 注解处理器不工作还是...? :) - Konstantin Yovkov
目前我运行 WEB 项目时,希望能够捕获 DB 项目(即 jar 包)中的注解处理器。但现在还没有实现。 - blagae
好的,我有一个可以建议的解决方案。请稍等一下,让我写下来。 :) - Konstantin Yovkov
还有一个问题,你在使用Eclipse吗? - Konstantin Yovkov
我实际上在这个项目中使用Netbeans,但我总是愿意改变。 - blagae
你可以使用反射库 https://code.google.com/p/reflections/ 从类路径中加载相关的包类,然后遍历它们并手动检查注释吗? - user467257
3个回答

8
注解处理器在项目(在您的情况下是WEB)的编译阶段工作,编译器编译这个项目。当前项目的依赖已经被编译,编译器(以及您的注解处理器)不会触及(或无法访问)第三方库(如DB)。
您可以尝试将注解处理器提取到单独的项目/ jar中,并在WEB和DB项目中使用它。在这种情况下,注解处理器将在具体项目的编译阶段创建CrudRepository。并且DB项目中生成的所有类都将在WEB中可用。

你说得对,注解处理器不会触及第三方库。这正是我的问题所在:如果有可能的话,如何强制访问依赖项... - blagae
@blagae,是的,这是不可能的,因为注解处理器只能使用源文件(.java),而不能使用已编译的文件(.class)。否则,Java编译器应该将已编译的代码反编译以进行处理 - 这是荒谬的。 - Denys Denysiuk
@blagae javac /path/to/sources \ -cp /path/to/classpath \ -processorpath /path/to/annotation-processor.jar @blagae javac /path/to/sources \ -cp /path/to/classpath \ -processorpath /path/to/annotation-processor.jar - Denys Denysiuk
请参见:http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#options - Denys Denysiuk
我必须接受这个答案,因为根据我所知,对于我的问题来说,正确的答案是“这是不可能的”。 - blagae
显示剩余2条评论

5

个人建议将注解处理器提取到单独的maven模块中,并从WEB模块添加依赖项。

然而,这对于成功触发注解处理器并不那么重要。

为了使注解处理器正常工作,您需要提供两件事情:

由于您提到当前没有生成任何类,我会假设您缺少meta文件。因此,打开您的WEB项目并导航到src/main/resouces文件夹。其中,您必须创建一个META-INF文件夹,并在其中嵌套一个services文件夹。然后,在services中创建一个名为javax.annotation.processing.Processor的文件。文件的内容应列出您的注解处理器的完全限定类名。如果有多个注解处理器,则完全限定类名应位于单独的行上。但是因为您只有一个注解处理器,所以您应该像这样写:

com.yourdomain.processor.RepositoryFactory

请注意,您需要将这一行更改为您注释处理器的实际完全合格类名。
最终,您应该得到类似的结构: enter image description here 此元文件非常重要,否则编译器将不知道用户定义的注释处理器。如果有它,它将使用所有已注册的处理器。
之后,当您执行 "mvn clean install" 时,所有模块都将被清理和构建。但是,由于编译器现在意识到您的注释处理器,因此它会触发它。默认情况下,所有生成的源都位于 "target/generated-sources" 文件夹中。此外,它们都在您在注释过程中配置的包下,即 "blagae.web.repo"。
为了在代码中使用生成的源代码,您必须将 "target/generated-sources" 添加到项目类路径中。如果您不想依赖 IDE 来执行此操作,可以通过将 "target/generated-sources" 添加到类路径来扩展 maven 中的 "build"。例如:
<build>
    <resources>
        ...
        <resource>
            <directory>${project.build.directory}/generated-resources</directory>
        </resource>
    </resources>
</build>

很抱歉这不起作用。问题仍然是预编译器只能在当前项目中选择有注释的类/方法(即尚未编译的类)。 - blagae
我已经在使用maven-processor-plugin,并且它确实在虚拟运行期间捕获了WEB本身中使用的其他注释(例如@Controller)。您的建议与maven-processor-plugin执行相同的操作,即对当前项目非常有效,但不会捕获依赖项。 - blagae

-1
在您的项目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

你也可以参考这个相关的问题:如何在包含为jar的不同项目模块中注入对象


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