【发布时间】: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 中逐步完成了代码,似乎 commandHandlerInstance 的 Instance 对象是 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<CommandHandler> instance = commandHandlerInstance.select(qualifier);,其中NullPointerException被抛出大概是因为Instance对象commandHandlerInstance是null
我在 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 @Startupbean,它工作正常。因此,我建议您的容器或应用程序的包装一定有问题。您的容器中是否有 CDI 工作?需要注意的一点是,对于 Web 存档, beans.xml 应该在 WEB-INF 而不是 META_INF 中。
标签: java jakarta-ee dependency-injection cdi jboss-weld