2015-05-15 4 views
12

Я создаю приложение Java SE на основе Gradle, основанное на Hibernate, как и мой ORM. Мой план состоит в том, чтобы использовать weld-se, чтобы иметь возможность использовать аннотации CDI для инъекций EntityManagers во всем приложении.Проблемы с обнаружением бобов при использовании сварного шва с плагином приложения Gradle

на основе общего HibernateUtil вспомогательного класса, найденного в документации Hibernate, я двинулся к JPA интерфейсов и добавил @Produces аннотаций для обеспечения методов производителей (я добавил пустой META-INF/beans.xml а):

package dao; 

import javax.enterprise.inject.Disposes; 
import javax.enterprise.inject.Produces; 
import javax.persistence.EntityManager; 
import javax.persistence.EntityManagerFactory; 
import javax.persistence.Persistence; 

public class HibernateUtil { 
    private static final EntityManagerFactory emf = buildEntityManagerFactory(); 

    private static EntityManagerFactory buildEntityManagerFactory() { 
     try { 
      return Persistence.createEntityManagerFactory("persistenceUnit"); 
     } catch (Throwable ex) { 
      System.err.println("Initial EntityManagerFactory creation failed." + ex); 
      throw new ExceptionInInitializerError(ex); 
     } 
    } 

    @Produces 
    public static EntityManager createEntityManager() { 
     return emf.createEntityManager(); 
    } 

    public static void closeEntityManager(@Disposes EntityManager em) { 
     System.out.println("Closing EM"); 
     try { 
      em.close(); 
     } catch (Throwable t) { 
      t.printStackTrace(); 
     } 
    } 
} 

Когда я попробуйте использовать @Inject аннотации на поле, однако, Weld не удается решить правильный метод производителя и производит исключение вместо:

исключения в потоке «основные» org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001308: Невозможно разрешить любые компоненты для Type: class app.DemoApplication; Квалификаторы: [@ javax.enterprise.inject.Any()] at org.jboss.weld.bean.builtin.InstanceImpl.get (InstanceImpl.java:101) at app.Main.main (Main.java:14)

вредоносный код, конкретизируется через контейнер Weld для поддержки CDI и невероятно просты:

package app; 

import javax.inject.Inject; 
import javax.persistence.EntityManager; 

public class DemoApplication { 
    @Inject private EntityManager em; 

    public void run() { 
     try { 
      em.getTransaction().begin(); 
      System.out.println("Inside transaction"); 
     } catch (Throwable t) { 
      t.printStackTrace(); 
     } finally { 
      em.getTransaction().rollback(); 
      em.close(); 
     } 
    } 
} 

Я отсутствует очевидный пункт здесь? Как я могу заставить Weld открыть метод производителя для ввода моих зависимостей?

Я собрал минимальный проект, воспроизводящий мою проблему на Github. Спасибо за любые полезные предложения! :)

Update 2015-05-18:

Кажется, я понял сообщение об ошибке. На самом деле, Weld даже не разрешает фасоль DemoApplication, что заставляет меня поверить, что что-то не так с процессом обнаружения бобов. После обновления моего шва-с зависимости от недавно выпущенной версии 3.0.0.Alpha8 (см связанного GitHub репо), я был в состоянии получить приложение для работы вручную говоря Weld о моих бобах в Main.java:

final Weld weld = new Weld() 
     .enableDiscovery() 
     .addPackage(false, HibernateUtil.class) 
     .addPackage(false, DemoApplication.class); 

Тем не менее, любые предложения относительно того, почему бобы не открываются автоматически, несмотря на наличие пустого META-INF/beans.xml, высоко оценены!

Update 2015-05-19:

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

+0

Что такое 'bean-discovery-mode' в вашем' beans.xml'? –

+0

@MilanBaran Я попытался использовать пустой beans.xml, а также один с 'bean-discovery-mode = ALL', оба без видимого эффекта. – AdrianoKF

ответ

14

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

:build задача создает два отдельных выходных папки для фактических результатов компиляции и и о выделении дополнительных ресурсов (build/classes и build/resources). Только при создании архива JAR эти две папки объединяются. Однако задача :run запускает приложение непосредственно из выходных папок компиляции с двумя отдельными записями классов классов для классов и ресурсов.

Механизм обнаружения связки Weld, по-видимому, только пытается обнаружить бобы для одного и того же входа в класс, как файл META-INF/beans.xml, который в этом случае является папкой build/resources/main. В свою очередь, никакие бобы никогда не обнаруживаются и никогда не имеют права на инъекцию в любом месте.

Мой Обойти сейчас (см репозиторий Git) является создание дополнительной задачи Gradle, чтобы скопировать ресурсы в соответствующую папку, так что открытие боб происходит на правильную запись пути к классам:

task copyResources(type: Copy) { 
    from "${projectDir}/src/main/resources" 
    into "${buildDir}/classes/main" 
} 

processResources.dependsOn copyResources 

То же проблема с подобным была описана на форумах Gradle: https://discuss.gradle.org/t/application-plugin-run-task-should-first-consolidate-classes-and-resources-folder-or-depend-on-installapp-or-stuff-like-weld-se-wont-work/1248

Спасибо всем за ваши подсказки!

+0

Спасибо, человек ... вы спасатель жизни – magicroomy

+0

Вы подали жалобу [WELD] (https://issues.jboss.org/projects/WELD) об этом странном поведении? – lucasvc

+0

Вместо копирования вы можете добавить папку ресурсов для запуска класса объектов. 'run { classpath + = файлы (" $ {buildDir}/resources ") } ' – serdroid

2

Существует проблема с объявлением static. Могли бы вы сделать это не статическим способом?

Относительно https://issues.jboss.org/browse/WELD-1505 следует исправить в 2.2.0.Beta2

+0

Я попытался преобразовать статические методы-производители в HibernateUtil в методы экземпляра, однако безрезультатно. Я пробовал как Weld 2.2.9.Final, так и 3.0.0.Alpha8, поэтому указанная вами проблема должна быть исправлена ​​в этих выпусках. Спасибо за предложение! – AdrianoKF

1

Насколько я мог видеть из вашего примера GIT, у вас есть что-то подобное в терминах иерархии объектов:

Main -> DemoApplication -> EntityManager 

Если вы нагнетание EntityManager на ваш DemoApplication, вы будете иметь для инъекции DemoApplication на ваш Main.java, иначе вы сломаете цепочку инъекций.

Try аннотировать DemoApplication и класс HibernateUtil с @Singleton, а затем вводят его на свой класс Main.java:

@Inject private EntityManager entity; 
@Inject private DemoApplication demo; 

Это должно помочь пропустить ручное обнаружение WELD боба.

Кроме того, если вы собираетесь создавать JAR, ваш beans.xml должен быть в META-INF, но если вы создаете WAR из своего проекта, beans.xml должен быть в WEB-INF ,

1

Решение также можно найти, указав sourceSets в градиенте.

Как это:

// Override Gradle defaults - a force an exploded JAR view 
sourceSets { 
    main { 
     output.resourcesDir = 'build/classes/main' 
     output.classesDir = 'build/classes/main' 
    } 
    test { 
     output.resourcesDir = 'build/classes/test' 
     output.classesDir = 'build/classes/test' 
    } 
} 

выглядит чище, на мой взгляд. И, конечно же, лучше объявить каждый класс как пакет.

0

В качестве улучшения: Gradle 4 использует другой выходной каталог для классов.

Определение задачи в other answer немного неверно. Глядя на Gradle журнал для запуска задачи, вот путь к классам используется:

Command: /home/rizalp/package/jdk1.8.0_141/bin/java -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -cp /home/rizalp/code/helloworld-cdi2-se/build/classes/java/main:/home/rizalp/code/helloworld-cdi2-se/build/resources/main:/home/rizalp/.gradle/caches/modules-2/files-2.1/org.glassfish.jersey.inject/jersey-cdi2-se/2.26-b09/c540e230762ab07ebb3d372ee13446419d70dd28/jersey-cdi2-se-2.26-b09.jar....... 

Так он использует /build/classes/java/main и затем /build/resources/main.Вот моя задача:

task copyResources(type: Copy) { 
    from "${projectDir}/src/main/resources/META-INF" 
    into "${buildDir}/classes/java/main/META-INF" 
} 

processResources.dependsOn copyResources 
+0

Улучшение. Gradle 4 использует разные выходные каталоги для классов –

+0

https://stackoverflow.com/a/30325614/1448852 –

Смежные вопросы