【问题标题】:How to programmatically lookup and inject a CDI managed bean where the qualifier contains the name of a class如何以编程方式查找和注入限定符包含类名的 CDI 托管 bean
【发布时间】:2015-06-14 03:47:59
【问题描述】:

我正在尝试以编程方式查找并注入一个 CDI 托管 bean,其中限定符包含一个类的名称(不是我要注入的类),但是我遇到的问题是我使用的代码查找正确的 bean 总是返回 null

我要注入的 bean 使用名为 @CQRSCommandHandler 的自定义注释进行注释,其中包含用作限定符的类的名称,并且 bean 还实现了一个名为 CommandHandler 的接口。我使用限定符的类实现了接口Command

基于我对 CDI 的了解有限,我相信为了以编程方式查找已使用 @CQRSCommandHandler 注释限定的正确 bean,我需要扩展 AnnotationLiteral,然后我可以使用 Instance 到选择 bean。

@CQRSCommandHandler注解的代码如下:

@Qualifier
@Documented
@Retention(value= RetentionPolicy.RUNTIME)
public @interface CQRSCommandHandler {
    Class<? extends Command> command();
}

扩展AnnotationLiteral的代码如下:

public class CQRSCommandHandlerQualifier extends AnnotationLiteral<CQRSCommandHandler> implements CQRSCommandHandler {

    private static final long serialVersionUID = 1L;
    private final Class<? extends Command> command;

    public CQRSCommandHandlerQualifier(Class<? extends Command> command) {
        this.command = command;
    }

    @Override
    public Class<? extends Command> command() {
        return command;
    }

}

我使用 CDI 查找正确 bean 的代码如下:

@Inject
@Any
private Instance<CommandHandler> commandHandlerInstance;

private CommandHandler findCommandHandlerFor(Command command) {

    CommandHandler commandHandler = commandHandlerInstance.select(new CQRSCommandHandlerQualifier(command.getClass())).get();  //This always returns null
    return commandHandler;
}

尽管谷歌搜索了很多小时,但我无法弄清楚为什么 commandHandlerInstance.select(new CQRSCommandHandlerQualifier(command.getClass())).get(); 没有返回一个已用 @CQRSCommandHandler (command = MyCommand.class) 注释的 bean 实例,其中 bean 实现了 CommandHandler 接口,MyCommand.class 实现了接口Command

这是以编程方式查找和注入限定符包含类名称的 CDI 托管 bean 的正确方法吗?如果是这样,上面的代码哪里出错了?如果不是,实现相同最终结果的最佳方法是什么?


更新

以下代码是我尝试查找的 bean 的示例实现:

@CQRSCommandHandler(command = CreateToDoItemCommand.class)
public class CreateToDoItemCommandHandler implements CommandHandler {

    @Override
    public <R> Object handle(Command command) {
        System.out.println("This is the CreateToDoItemCommandHandler");
        return null;
    }
}

以下代码是CommandHandler的接口:

public interface CommandHandler {

    public <R> Object handle(Command command);

}

以下代码是我在限定符中用作参数的类的示例:

public class CreateToDoItemCommand implements Command {

    private String todoId;
    private String description;

    public CreateToDoItemCommand(String todoId, String description) {
        this.todoId = todoId;
        this.description = description;
    }

    public String getTodoId() {
        return todoId;
    }

    public String getDescription() {
        return description;
    }

}

我已经在 Eclipse 中逐步完成了代码,似乎 commandHandlerInstanceInstance 对象是 null


更新 2

正如@redge 所建议的,我已将实例化的每个步骤分开到单独的行中,如下所示:

private CommandHandler findCommandHandlerFor(Command command) {
    CQRSCommandHandlerQualifier qualifier = new CQRSCommandHandlerQualifier(command.getClass());
    Instance<CommandHandler> instance = commandHandlerInstance.select(qualifier);
    CommandHandler commandHandler = instance.get();
    return commandHandler;
}

问题似乎在于这行代码Instance&lt;CommandHandler&gt; instance = commandHandlerInstance.select(qualifier);,其中NullPointerException被抛出大概是因为Instance对象commandHandlerInstancenull

我在 Weld 2.0.0 SP1 附带的 GlashFish 4 上运行此代码,但我也刚刚尝试在 GlashFish 4.1 上运行相同的代码并安装了 Weld 版本 2.2.10.SP1,这是最新的Maven Central,但同样的问题发生了。

【问题讨论】:

  • 我认为您需要在这个问题中添加一些内容。首先,您能否提供一个失败的示例实现?其次,是对get() 的调用返回null,还是Instance 对象为null?您在这里的模式是一个非常标准的模式,并且效果很好。你有 beans.xml 吗?
  • 我已更新问题以提供失败的示例实现。 Instance 对象为空,所以我想这可能是问题的根本原因。我忘记将它包含在更新的问题中,但我在 META-INF 目录中有一个 beans.xml 文件。
  • 您的 CDI 容器可能有问题。你用的是哪个容器?如果您的容器配置正确,您的 CommandHandler 类可能会从类路径中丢失,或者无法将 beans.xml 文件添加到包含注入 bean 类的 每个 jar 文件。将实例化的每个步骤分隔到单独的行中,以确保哪个对象为空。
  • 我已经更新了问题,将实例化的每个步骤分开到单独的行上。我还检查了所有包含我要注入的 bean 的罐子都有一个 beans.xml 文件。
  • 我已将您的代码复制到一个 Web 项目中,并将其部署在 JBOSS 7.1 (JEE6) 和 Glassfish4.1 (JEE7) 上。我从 @PostConstruct 方法中引入了一个名为 findCommandHandlerFor(command)@Singleton @Startup bean,它工作正常。因此,我建议您的容器或应用程序的包装一定有问题。您的容器中是否有 CDI 工作?需要注意的一点是,对于 Web 存档, beans.xml 应该在 WEB-INF 而不是 META_INF 中。

标签: java jakarta-ee dependency-injection cdi jboss-weld


【解决方案1】:

您拥有 GlassFish 4.1。我怀疑你有一个 beans.xml 文件,如果你有,应该根据你当前的设置将其标记为bean-discovery-mode="all"。如果你不这样做,或者你使用bean-discovery-mode="annotated",那么你需要为你的每个命令添加一个bean定义注释,例如@ApplicationScoped 用于每个命令,以便它们可以被解析。

【讨论】:

  • 我在 beans.xml 文件中使用以下内容:&lt;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 http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" version="1.1" bean-discovery-mode="all"&gt; &lt;/beans&gt; @Inject @Any private Instance&lt;CommandHandler&gt; commandHandlerInstance;null 与问题有关吗?
  • 如果您可以在注入实例的示例中添加更多详细信息,那就太好了。那是怎么注入的?
猜你喜欢
  • 2022-10-13
  • 1970-01-01
  • 2011-10-08
  • 2014-09-08
  • 2014-02-27
  • 2015-08-06
  • 1970-01-01
  • 2013-09-01
  • 2011-10-16
相关资源
最近更新 更多