Java 将weld se与Gradle应用程序插件一起使用时的Bean发现问题

Java 将weld se与Gradle应用程序插件一起使用时的Bean发现问题,java,gradle,jboss-weld,weld-se,Java,Gradle,Jboss Weld,Weld Se,我正在构建一个基于Gradle的JavaSE应用程序,它构建在Hibernate之上,作为我选择的ORM。我的计划是使用weld se,以便能够在整个应用程序中使用CDI注释注入EntityManager 基于Hibernate文档中常见的HibernateUtilhelper类,我转向JPA接口并添加了@products注释以提供生产者方法(我还添加了一个空的META-INF/beans.xml): 但是,当我尝试在字段上使用@Inject注释时,Weld无法解析正确的生产者方法,而是生成异常

我正在构建一个基于Gradle的JavaSE应用程序,它构建在Hibernate之上,作为我选择的ORM。我的计划是使用
weld se
,以便能够在整个应用程序中使用CDI注释注入
EntityManager

基于Hibernate文档中常见的
HibernateUtil
helper类,我转向JPA接口并添加了
@products
注释以提供生产者方法(我还添加了一个空的
META-INF/beans.xml
):

但是,当我尝试在字段上使用
@Inject
注释时,Weld无法解析正确的生产者方法,而是生成异常:

线程“main”中出现异常 org.jboss.weld.exceptions.unsatifiedResolutionException:weld-001308: 无法解析类型为class app.DemoApplication的任何bean; 限定符:[@javax.enterprise.inject.Any()] 位于org.jboss.weld.bean.builtin.InstanceImpl.get(InstanceImpl.java:101) 位于app.Main.Main(Main.java:14)

有问题的代码是通过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发现注入依赖项的生产者方法

我已经制定了一个最小的项目,重现我的问题。谢谢你的建议!:)

更新2015-05-18:

好像我误解了错误信息。事实上,Weld甚至没有解析
DemoApplication
bean,这让我相信,bean发现过程出了问题。在将weld se依赖项更新到新发布的3.0.0.Alpha8版本(参见链接的Github repo)后,我能够通过在
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:


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

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


关于应该在2.2.0.Beta2中修复的问题,从GIT示例中我可以看到,在对象的层次结构方面,您有如下内容:

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

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

@Inject private EntityManager entity;
@Inject private DemoApplication demo;
这将有助于跳过手动焊豆检测


另外,如果您要制作一个JAR,那么beans.xml应该是META-INF格式,但是如果您要在项目中打一场战争,那么beans.xml必须是WEB-INF格式。

在这样一个问题上花费了太多的时间,我终于找到了问题的根源。它与Weld和Jandex无关,而是与Gradle构建其输出目录的方式有关:

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

Weld的bean发现机制显然只尝试为与
META-INF/beans.xml
文件相同的类路径条目发现bean,在本例中,该文件是
build/resources/main
文件夹。反过来,没有发现豆类,也没有资格在任何地方注射

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

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

processResources.dependsOn copyResources
Gradle论坛上也描述了同样的问题和类似的问题:


谢谢大家的提示

也可以通过在gradle中定义源集来找到解决方案

像这样:

// 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'
    }
}

在我看来,看起来更干净。当然,最好是将每个类声明为一个包

作为改进:Gradle4为类使用了不同的输出目录

中的任务定义稍有错误。查看运行任务的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

我已经尝试将HibernateUtil中的静态生产者方法转换为实例方法,但是没有效果。我已经尝试了Weld 2.2.9.Final和3.0.0.Alpha8,因此您提到的问题应该在这些版本中得到解决。还是谢谢你的建议!您的
beans.xml
中的
bean发现模式是什么?@MilanBaran我尝试使用一个空的beans.xml,以及一个
bean发现模式=ALL
,两者都没有任何明显的效果。您是否对这种奇怪的行为提出过任何问题?您可以添加resources文件夹来运行任务的类路径,而不是复制<代码>运行{classpath+=文件(${buildDir}/resources”)}
改进。Gradle 4为类使用不同的输出目录
task copyResources(type: Copy) {
    from "${projectDir}/src/main/resources/META-INF"
    into "${buildDir}/classes/java/main/META-INF"
}

processResources.dependsOn copyResources