层次结构中类型的 CDI 注入

CDI injection of types in heirarchy

我有一个超类型 (CaseDTO) 和我要注入的几个子类型(GroupCaseDTO、IPCaseDTO)。开发环境是JDev 12c.

这里是两个注入点:

  1. 注入超类型(此注入点适用于两种子类型):

    @Inject @CaseContext 私人案例DTO muwCase;

  2. 注入特定子类型(此注入点仅适用于一种子类型):

    @Inject @CaseContext 私人 GroupCaseDTO muwCase;

为了获得这些值,我尝试设置了一些生产者方法:

// supertype
@Produces @CaseContext
public CaseDTO getContextCase()  {
    return JSFUtils.getFromPageFlowScope("case", CaseDTO.class);
}

// subtype 1
@Produces @CaseContext
public IPCaseDTO getContextIpCase()  {
    return JSFUtils.getFromPageFlowScope("case", IPCaseDTO.class);
}

// subtype 2
@Produces @CaseContext
public GroupCaseDTO getContextGroupCase()  {
    return JSFUtils.getFromPageFlowScope("case", GroupCaseDTO.class);
}

问题是,如果我只包含超类型生产者方法,那么子类型注入点将不起作用:

:org.jboss.weld.exceptions.DeploymentException:WELD-001408: Unsatisfied dependencies for type GroupCaseDTO with qualifiers @CaseContext
at injection point [BackedAnnotatedField] @Inject @CaseContext private ca.bluecross.ab.muw.view.controller.decision.ManageGrpDecisionController.muwCase
at ca.bluecross.ab.muw.view.controller.decision.ManageGrpDecisionController.muwCase(ManageGrpDecisionController.java:0)
WELD-001475: The following beans match by type, but none have matching qualifiers:
- Managed Bean [class ca.bluecross.ab.muw.model.type.dto.grp.GroupCaseDTO] with qualifiers [@Any @Default]

但是如果我包含所有三个生产者方法,我会在超类型注入点得到这个异常:

:org.jboss.weld.exceptions.DeploymentException:WELD-001409: Ambiguous dependencies for type CaseDTO with qualifiers @CaseContext
at injection point [BackedAnnotatedField] @Inject @CaseContext private ca.bluecross.ab.muw.view.controller.UploadAssociatedDocumentController.muwCase
at ca.bluecross.ab.muw.view.controller.UploadAssociatedDocumentController.muwCase(UploadAssociatedDocumentController.java:0)
Possible dependencies: 
  - Producer Method [CaseDTO] with qualifiers [@CaseContext @Any] declared as [[BackedAnnotatedMethod] @Produces @CaseContext public ca.bluecross.ab.muw.view.util.DataContextHelper.getContextCase()],
  - Producer Method [GroupCaseDTO] with qualifiers [@CaseContext @Any] declared as [[BackedAnnotatedMethod] @Produces @CaseContext public ca.bluecross.ab.muw.view.util.DataContextHelper.getContextGroupCase()],
  - Producer Method [IPCaseDTO] with qualifiers [@CaseContext @Any] declared as [[BackedAnnotatedMethod] @Produces @CaseContext public ca.bluecross.ab.muw.view.util.DataContextHelper.getContextIpCase()]

只有两个子类型生产者方法也不起作用:

:org.jboss.weld.exceptions.DeploymentException:WELD-001409: Ambiguous dependencies for type CaseDTO with qualifiers @CaseContext
  at injection point [BackedAnnotatedField] @Inject @CaseContext private ca.bluecross.ab.muw.view.controller.UploadAssociatedDocumentController.muwCase
  at ca.bluecross.ab.muw.view.controller.UploadAssociatedDocumentController.muwCase(UploadAssociatedDocumentController.java:0)
  Possible dependencies: 
  - Producer Method [GroupCaseDTO] with qualifiers [@CaseContext @Any] declared as [[BackedAnnotatedMethod] @Produces @CaseContext public ca.bluecross.ab.muw.view.util.DataContextHelper.getContextGroupCase()],
  - Producer Method [IPCaseDTO] with qualifiers [@CaseContext @Any] declared as [[BackedAnnotatedMethod] @Produces @CaseContext public ca.bluecross.ab.muw.view.util.DataContextHelper.getContextIpCase()]

我想我可以通过为我需要的每个子类型使用限定符注释来解决这个问题,但这似乎太过分了。如果没有大量限定符注释,是否无法使注入工作?

事实上,这就是我想要的:一个生产者方法 + 一个允许我注入上下文大小写的限定符注释,而不管它是什么子类型。同样,我认为 CDI 不可能做到这一点,因为它(太)强类型化了。

我会在这方面做点什么:

public abstract class CaseDTO {
}

和子class,创建限定符。

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER})
public @interface CaseContext {

    @Nonbinding
    String value() default "";

}

@ApplicationScoped
public class CaseDTOProducer {

    @ApplicationScoped
    @CaseContext("")
    public CaseDTO produce(InjectionPoint ip) {
        //You can make the instantiation as complex as you want:
        final CaseContext caseContext = ip.getAnnotated().getAnnotation(CaseContext.class);
        switch (caseContext.value()) {
            case "ip":
                return new IpCaseDTO();
            case "group":
                return new GroupCaseDTO();
            default:
                throw new ContextException("Unknown Case Context");
        }
    }

}

那么注入点:

public class CaseService {

    @Inject 
    @CaseContext("ip")
    private CaseDTO caseDTO;

    @Inject 
    @CaseContext("group")
    private CaseDTO caseDTO;
}

我想你可能误解了typesafe resolution mechanism。 CDI 注入基于一组 bean(在您的情况下,是它们的生产者)和一组注入点(又名 IP;您 @Inject 所在的位置)。现在这两者都有特定的 类型和限定符集 .

为了注入 IP,您需要一个包含 所需类型 所有请求的限定符都是该 bean 提供的限定符子集的 bean .

Bean types, in case of producers,都是派生自return类型的方法。在您的情况下,它将是 class、每个超级 class 和所有已实现的接口(直接或间接)。

所以在你的代码示例中,如果你只是 @Inject CaseDTO,你将得到 Ambiguous dependency 异常 - 所有三个生产者都生产一个 bean, 在其他类型中 , 还包含 CaseDTO.

另一方面,获得 Unsatisfied dependency 异常意味着您没有适合此类注入点的 bean。在您的示例中,只有超类型生产者意味着 @Inject GroupCaseDTO 将失败并出现此异常,因为 GroupCaseDTO 不在您的生产者创建的 bean 类型中。

至于您的问题的解决方案 - 您肯定可以使用限定符。 解决了注入超类型 的问题。 @maress 在另一个答案中提出的建议也是一个好主意(例如,没有新的限定符并使用现有限定符中的值)

In fact, here's what I want: one producer method + one qualifier annotation that allows me to inject the context case

您可以使用一个限定符给出不同的结果,但您需要根据其他 属性 来确定您想要的结果。例如。你可以拥有:

@Produces
@CaseContext
public CaseDTO produce() {
  if (methodToDetermineConfiguration()) {
    return JSFUtils.getFromPageFlowScope("case", IPCaseDTO.class);
  } else {
    return JSFUtils.getFromPageFlowScope("case", GroupCaseDTO.class);
  }
}

当然,您需要以某种方式定义 methodToDetermineConfiguration() 以了解您实际想要生产什么。不确定您有哪些选择,只是分享有关如何设置制作人的信息。

我认为这是我能做的最好的了;适应@maress

@Produces @CaseContext
public CaseDTO getContextCase()  {
    return JSFUtils.getFromPageFlowScope("case", CaseDTO.class);
}

@Produces @CaseContext("ip")
public IPCaseDTO getContextIpCase()  {
    return JSFUtils.getFromPageFlowScope("case", IPCaseDTO.class);
}

@Produces @CaseContext("group")
public GroupCaseDTO getContextGroupCase()  {
    return JSFUtils.getFromPageFlowScope("case", GroupCaseDTO.class);
}

注解:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ FIELD, METHOD })
public @interface CaseContext {
    public String value() default "";
}

然后注入点

@Inject @CaseContext
private CaseDTO muwCase;

@Inject @CaseContext("group")
private GroupCaseDTO muwCase;

@Inject @CaseContext("ip")
private IPCaseDTO muwCase;