Обработчик Java-аннотации для удаленного JAR

Общий вопрос

У меня есть два проекта A и B; B имеет зависимость от A. Я хочу сгенерировать некоторый код в B с помощью обработчика аннотации, основанный на аннотации на объектах в A. Когда я запускаю компиляцию с правильной реализацией процессора, выбираются только аннотированные объекты из B.

Я понимаю, что сканирование других JAR файлов должно быть отключено по умолчанию, потому что вы обычно не хотите выполнять аннотирование для всех ваших зависимостей. Я также понимаю, что может быть невозможно сделать то, что я хочу сделать из-за магии компилятора, о которой я не очень много знаю, но я надеюсь, что это не так.

Конкретный случай

Мои проекты называются DB и WEB. WEB, очевидно, зависит от DB для доступа к JPA; это настроено в Maven. Из-за ряда архитектурных вариантов, DB должен оставаться отдельным JAR. БД не использует Spring, за исключением некоторых аннотаций, которые потребляются WEB; WEB использует Spring MVC.

Я пытаюсь создать интерфейсы CrudRepository для всех моих объектов JPA с помощью обработчика аннотаций. Предполагается, что объекты @Repository входят в пакет 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. Я также опасаюсь конфигурации, которая может потребоваться для выполнения этой работы.

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>

SUGGESTION

Еще одна случайная мысль, которую я имел, заключалась в том, чтобы запустить процесс JavaCompiler для проекта БД в WEB, но как бы я ввел свой Processor?

Ответ 1

Обработчик аннотации работает на этапе компиляции вашего проекта (WEB в вашем случае), а компилятор компилирует этот проект. Зависимости текущего проекта уже скомпилированы, а компилятор (и, как результат, обработчик аннотаций) не касается (или не имеет доступа) сторонних библиотек (БД).

Вы можете попытаться извлечь обработчик аннотации в отдельный проект/банку и использовать его в проектах WEB и DB. В этом случае обработчик аннотации создаст CrudRepository на этапе компиляции конкретного проекта. И все созданные классы в проекте БД будут доступны в WEB.

Ответ 2

Лично я извлечу процессор аннотации в отдельный модуль maven и добавлю к нему зависимость от WEB-модуля.

Однако это не имеет большого значения для успешного запуска обработчика аннотаций.

Чтобы обработать обработчик аннотаций, вам нужно предоставить две вещи:

Поскольку вы упомянули, что в настоящее время классы не генерируются, я бы предположил, что вам не хватает метафайла. Итак, откройте свой веб-проект и перейдите в папку src/main/resouces. Внутри вы должны создать папку META-INF с вложенной папкой services. Затем в services создайте файл с именем javax.annotation.processing.Processor. Содержимое файла должно содержать список полнофункциональных имен (-ов) вашего аннотационного процессора. Если имеется более одного обработчика аннотаций, то полные имена классов должны быть в отдельных строках. Но поскольку у вас есть только один, у вас будет что-то вроде:

com.yourdomain.processor.RepositoryFactory

Обратите внимание, что вам придется изменить эту строку с фактическим полностью quallified имя класса вашего обработчика аннотаций.

В конце концов, вы должны получить аналогичную структуру:

enter image description here

Этот метафайл является важным, поскольку в противном случае компилятор не знает о пользовательских обработчиках аннотаций. При этом он будет использовать все зарегистрированные процессоры.

После этого, когда вы выполните mvn clean install, все ваши модули будут очищены и построены. Однако, поскольку компилятор теперь будет знать ваш процессор аннотаций, он вызовет его. Все созданные источники будут расположены (по умолчанию) в папке target/generated-sources. Более того, все они будут находиться под пакетом, который вы настроили в процессе аннотации, т.е. blagae.web.repo.

Чтобы использовать сгенерированные источники в вашем коде, вам нужно добавить targer/generated-sources в путь к классу проекта. Если вы не хотите полагаться на IDE для этого, вы можете расширить maven <build>, добавив target/generated-sources в путь к классам. Что-то вроде:

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

Ответ 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="#" onclick="location.href='http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd'; return false;">http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"</a>
       version="1.1" bean-discovery-mode="all">
</beans>

и попробуйте

перейдите по ссылке Java EE 7 Deployment Descriptors для более подробной информации

вы должны использовать javaee 7/CDI 1.1

Редакция: пожалуйста, прочтите ниже how-to-inject-object-from-different-project-module-included-as-jar