Обработчик 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