【问题标题】:Bean discovery problems when using weld-se with Gradle application plugin将weld-se 与Gradle 应用程序插件一起使用时的Bean 发现问题
【发布时间】:2015-07-27 03:46:24
【问题描述】:

我正在构建一个基于 Gradle 的 Java SE 应用程序,该应用程序基于 Hibernate 作为我选择的 ORM。我的计划是使用weld-se 以便能够使用CDI 注释在整个应用程序中注入EntityManagers

基于 Hibernate 文档中常见的 HibernateUtil 帮助类,我转向 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 无法解析正确的生产者方法并产生异常:

线程“main”中的异常 org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001308: 无法解析类型的任何 bean:类 app.DemoApplication; 限定符:[@javax.enterprise.inject.Any()] 在 org.jboss.weld.bean.builtin.InstanceImpl.get(InstanceImpl.java:101) 在 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 上建立了一个最小的项目来重现我的问题。感谢您提供任何有用的建议! :)

2015-05-18 更新:

好像我误解了错误信息。事实上,Weld 甚至没有解析 DemoApplication bean,这让我相信 bean 发现过程有问题。在将我的weld-se 依赖项更新到新发布的3.0.0.Alpha8 版本(请参阅链接的Github 存储库)后,我能够通过在Main.java 中手动告诉Weld 我的bean 来让应用程序工作:

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

尽管如此,对于尽管有一个空的 META-INF/beans.xml 并没有自动发现 bean 的任何建议,我们都非常感谢!

2015-05-19 更新:

谜团已解开,请看下面我自己的答案。我更改了问题标题,以反映问题的实际性质。

【问题讨论】:

  • 您的beans.xml 中的bean-discovery-mode 是什么?
  • @MilanBaran 我尝试使用一个空 beans.xml,以及一个带有 bean-discovery-mode=ALL 的文件,两者都没有任何可见效果。

标签: java gradle jboss-weld weld-se


【解决方案1】:

在这样的问题上花费了比看起来更理智的时间,我终于能够找到问题的根源。它与 Weld 或 Jandex 无关,而是 Gradle 构建其输出目录的方式:

:build 任务为实际编译结果和其他资源(build/classesbuild/resources)创建两个单独的输出文件夹。只有当您创建 JAR 存档时,这两个文件夹才会合并。但是,:run 任务直接从编译输出文件夹启动应用程序,其中包含两个单独的类和资源类路径条目。

Weld 的 bean 发现机制显然只尝试发现与 META-INF/beans.xml 文件相同的类路径条目的 bean,在本例中为 build/resources/main 文件夹。反过来,从未发现任何 bean,也永远无法在任何地方注入。

我目前的解决方法(请参阅 Git 存储库)是创建一个额外的 Gradle 任务以将资源复制到适当的文件夹中,以便 bean 发现发生在正确的类路径条目上:

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

感谢大家的提示!

【讨论】:

  • 您是否针对这种奇怪的行为提交了任何WELD 问题?
  • 你可以添加资源文件夹而不是复制来运行任务的类路径。 run { classpath += files("${buildDir}/resources") }
【解决方案2】:

static 声明存在问题。你能用非静态的方式来做吗?

关于https://issues.jboss.org/browse/WELD-1505应该在2.2.0.Beta2中修复

【讨论】:

  • 我尝试将 HibernateUtil 中的静态生产者方法转换为实例方法,但无济于事。 Weld 2.2.9.Final 和 3.0.0.Alpha8 我都试过了,所以你提到的问题应该在这些版本中得到修复。仍然感谢您的建议!
【解决方案3】:

据我从您的 GIT 示例中可以看出,就对象的层次结构而言,您有类似的情况:

Main -> DemoApplication -> EntityManager

如果要将 EntityManager 注入到 DemoApplication 中,则必须将 DemoApplication 注入到 Main.java 中,否则会破坏注入链。

尝试使用 @Singleton 注释您的 DemoApplication 和 HibernateUtil 类,然后将其注入到您的 Main.java 类中:

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

这应该有助于跳过手动 WELD bean 检测。

另外,如果你要生成一个 JAR,你的 beans.xml 应该在 META-INF 中,但是如果你从你的项目中制作一个 WAR,beans.xml 必须在 WEB-INF 中。

【讨论】:

    【解决方案4】:

    也可以通过在 gradle 中定义 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'
        }
    }
    

    在我看来看起来更干净。并且肯定比将每个类声明为一个包更好。

    【讨论】:

      【解决方案5】:

      作为一项改进: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
      

      【讨论】:

      • 改进。 Gradle 4 为类使用不同的输出目录
      猜你喜欢
      • 2023-03-06
      • 2016-04-22
      • 2019-07-24
      • 2011-10-12
      • 1970-01-01
      • 2017-03-21
      • 1970-01-01
      • 2016-06-22
      • 2020-11-13
      相关资源
      最近更新 更多