在上下文和依赖注入 (CDI) 不断发展的环境中,开发人员经常遇到与 bean 命名、默认实现和潜在冲突相关的障碍。本文详细探讨了 CDI 中与 @Named 注释相关的潜在陷阱。我们将深入研究其复杂性,阐明有问题的场景,并讨论替代方法,包括使用 SmallRye 中的 @Identifier。此外,我们将提供有关构建强大且可维护的最佳实践的见解Jakarta EE
应用程序。
@Default 注释是 CDI 中的一个有价值的工具,用于将特定实现显式标记为给定接口或 bean 类型的默认实现。它在处理同一接口的多个实现时发挥作用,允许开发人员指定在不使用其他限定符时默认应注入哪个实现。
考虑存在 GreetingService 接口的多个实现的场景:
@Default public class DefaultGreetingService implements GreetingService { @Override public String greet(String name) { return "Hello, " name; } }
public class SpecialGreetingService implements GreetingService { @Override public String greet(String name) { return "Greetings, " name "!"; } }
在不指定任何限定符的情况下注入 bean 时,CDI 使用 @Default 标记的 bean 作为默认值。这在具有多种实现的场景中是有益的,提供了明确的默认选择。
@Inject private GreetingService greetingService; // Injects the @Default implementation
虽然 @Default 的使用是可选的,但强烈建议使用它,特别是在处理具有多个实现的接口时。它提供了清晰一致的默认选项,防止 Bean 注入期间出现歧义和意外行为。
@Named 限定符在 CDI 中发挥着基础作用,为 bean 分配人类可读的名称或标识符。开发人员在将 bean 注入其他组件时经常使用它来通过名称引用 bean。
然而,@Named 也有其自身的一系列挑战,特别是在没有额外限定符的情况下使用时。默认情况下,CDI 将非限定类名关联为 bean 名称。这可能会导致与 @Default 限定符发生冲突,从而导致 bean 注入期间出现意外行为。
@Named public class MyBean { // Implementation }
在没有显式限定符的情况下注入 MyBean 时,CDI 将仅添加 @Named 限定符,而不是 @Default 限定符。 @Default 限定符仅在 bean 或其限定符上显式指定时才应用。
@Inject private MyBean myBean;
在这种情况下,如果存在具有相同类型名称的其他 bean,则可能会出现歧义。例如,如果有另一个名为 MyBean 的 bean,则注入将导致歧义。
为了解决这个问题,开发人员应该明确限定他们打算注入的 bean。
@Inject @Named("myBean") private MyBean myBean;
或者,开发人员可以为每个 bean 使用自定义限定符来消除歧义。
当在没有附加限定符的情况下使用@Named并且存在相同类型的多个实现时,会出现歧义。考虑以下场景:
@Named public class ServiceA implements Service { // Implementation }
@Named public class ServiceB implements Service { // Implementation }
在没有显式限定符的情况下注入服务可能会导致歧义,因为两个 bean 按类型匹配,并且没有名称或限定符区分它们。
@Inject private Service service;
在这种情况下,CDI不会隐式添加@Default或尝试解决歧义,从而导致由于不明确的依赖关系而导致注入失败。
认识到@Named 带来的挑战,开发人员经常寻求替代方案来更明确地控制 Bean 标识。其中一种替代方案是
中的 @Identifier 注释
小黑麦常见。此注释提供了一种更清晰、更可控的 bean 命名方法,减少了冲突和意外默认的风险。与 @Named 不同,@Named 要求每个应用程序都有唯一的值,@Identifier 允许多个 bean 具有相同的标识符值,只要它们的类型不同。在处理相同接口或相关类型的不同实现时,这种灵活性特别有用。
要使用@Identifier,只需用该注解注释bean类并指定标识符值即可:
@Identifier("payment") public class DefaultPaymentProcessor implements PaymentProcessor { // Implementation }
@Identifier("payment") public class LegacyPaymentGateway implements PaymentGateway { // Implementation }
使用@Identifier注入bean很简单:
public class Client { @Inject @Identifier("payment") PaymentProcessor processor; @Inject @Identifier("payment") PaymentGateway gateway; }
此处,“付款”@Identifier 值被多个 bean 重用,因为 PaymentProcessor 和 PaymentGateway 的类型不同。 @Named 不允许这种灵活性,其中
值在应用程序范围内必须是唯一的。
@Named 的另一种替代方法是创建自定义限定符。自定义限定符是用户定义的注释,可用于识别和限定 bean。它们提供对 Bean 选择的最精细控制,并且可以根据应用程序的特定需求进行定制。
要创建自定义限定符,请按照下列步骤操作:
例如,以下名为 DefaultPaymentGateway 的自定义限定符表示默认支付网关实现:
@Qualifier @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface DefaultPaymentGateway { }
要使用自定义限定符,请用它注释 bean 类:
@DefaultPaymentGateway public class StandardPaymentGateway implements PaymentGateway { // Implementation }
public class ExpressPaymentGateway implements PaymentGateway { // Implementation }
然后,使用限定符注入bean:
@Inject @DefaultPaymentGateway private PaymentGateway paymentGateway;
bean 识别的最佳方法取决于应用程序的具体需求。对于简单的应用程序,@Named 可能就足够了。对于更复杂的应用程序,@Identifier 或
自定义限定符提供更多控制和灵活性。
下表总结了每种方法的优缺点:
方法 | 优点 | 缺点 |
---|---|---|
@命名 | 简单,广泛支持 | 可能不明确,与@Default冲突 |
@标识符 | 标识更清晰,与@Default不冲突 | 需要额外注释 |
自定义限定符 | 最大灵活性,细粒度控制 | 需要前期工作来定义和维护 |
进一步确认,可以参考官方CDI规范
总之,与 @Named 相关的潜在陷阱强调了在 CDI 中使用此注释时需要仔细考虑。当依赖隐式命名时,尤其是在存在多个实现的情况下,可能会出现歧义和意外的默认值。鼓励开发人员探索替代方案,例如来自 SmallRye Common 的 @Identifier,以获得更受控制和更明确的 Bean 识别方法。采用显式限定、自定义限定符和替代方法可确保更流畅、更可控的 CDI 体验,从而实现健壮且可维护的 Java。
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3