」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 與 @Named 一起揭開挑戰

與 @Named 一起揭開挑戰

發佈於2024-09-02
瀏覽:608

在上下文和依赖注入 (CDI) 不断发展的环境中,开发人员经常遇到与 bean 命名、默认实现和潜在冲突相关的障碍。本文详细探讨了 CDI 中与 @Named 注释相关的潜在陷阱。我们将深入研究其复杂性,阐明有问题的场景,并讨论替代方法,包括使用 SmallRye 中的 @Identifier。此外,我们将提供有关构建强大且可维护的最佳实践的见解Jakarta EE
应用程序。

理解@Default

@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——一把双刃剑

@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或尝试解决歧义,从而导致由于不明确的依赖关系而导致注入失败。

替代方案:从 SmallRye Common 引入 @Identifier

认识到@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 选择的最精细控制,并且可以根据应用程序的特定需求进行定制。

要创建自定义限定符,请按照下列步骤操作:

  1. 定义一个新的注释类。
  2. 用@Qualifier注解注解类。
  3. (可选)为限定符提供默认值。

例如,以下名为 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规范

Unveiling Challenges with @Named

结论:Bean 命名和默认值的策略选择

总之,与 @Named 相关的潜在陷阱强调了在 CDI 中使用此注释时需要仔细考虑。当依赖隐式命名时,尤其是在存在多个实现的情况下,可能会出现歧义和意外的默认值。鼓励开发人员探索替代方案,例如来自 SmallRye Common 的 @Identifier,以获得更受控制和更明确的 Bean 识别方法。采用显式限定、自定义限定符和替代方法可确保更流畅、更可控的 CDI 体验,从而实现健壮且可维护的 Java。

版本聲明 本文轉載於:https://dev.to/yanev/unveiling-challenges-with-named-67p?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 在 Hacktoberfest 中做出貢獻的新方式:直接在前端 AI
    在 Hacktoberfest 中做出貢獻的新方式:直接在前端 AI
    Hacktoberfest 又回來了,今年為開發者帶來了一種令人興奮的新參與方式。 您現在可以直接透過 Webcrumbs 平台上的 Frontend AI 建立和提交模板,而不是傳統的 GitHub Pull 請求。只需前往tools.webcrumbs.org,對模板進行編碼,然後在準備好後點擊...
    程式設計 發佈於2024-11-07
  • 為什麼使用不帶括號的函數指標時“cout”列印“1”?
    為什麼使用不帶括號的函數指標時“cout”列印“1”?
    為什麼「cout a function without call it (not f() but f;). Print 1 Always?」在此程式碼中,程式碼嘗試在不使用括號的情況下「呼叫」名為pr 的函數。然而,這實際上並不是呼叫該函數。相反,它將函數指標傳遞給 cout 函數。當函數指標隱式轉...
    程式設計 發佈於2024-11-07
  • 讓您的網頁更快
    讓您的網頁更快
    什么是 DOM?它吃什么? DOM(文档对象模型)是网页及其开发的基础。它是 HTML 和 XML 文档的编程接口,以树状对象表示文档的结构。有树枝和树叶。文档中的每个元素、属性和文本片段都成为该树中的一个节点。它允许 JavaScript 与 HTML 元素交互、修改它们或添加新...
    程式設計 發佈於2024-11-07
  • JavaScript 中的 require 與 import
    JavaScript 中的 require 與 import
    我記得當我開始編碼時,我會看到一些js檔案使用require()來匯入模組和其他檔案使用import。這總是讓我感到困惑,因為我並不真正理解其中的差異是什麼,或者為什麼專案之間存在不一致。如果您想知道同樣的事情,請繼續閱讀! 什麼是 CommonJS? CommonJS 是一組用於...
    程式設計 發佈於2024-11-07
  • 使用鏡像部署 Vite/React 應用程式:完整指南
    使用鏡像部署 Vite/React 應用程式:完整指南
    在 GitHub Pages 上部署 Vite/React 应用程序是一个令人兴奋的里程碑,但这个过程有时会带来意想不到的挑战,特别是在处理图像和资产时。这篇博文将引导您完成整个过程,从最初的部署到解决常见问题并找到有效的解决方案。 无论您是初学者还是有经验的人,本指南都将帮助您避免常见的陷阱,并...
    程式設計 發佈於2024-11-07
  • 我如何在我的 React 應用程式中優化 API 呼叫
    我如何在我的 React 應用程式中優化 API 呼叫
    作为 React 开发者,我们经常面临需要通过 API 同步多个快速状态变化的场景。对每一个微小的变化进行 API 调用可能效率低下,并且会给客户端和服务器带来负担。这就是去抖和巧妙的状态管理发挥作用的地方。在本文中,我们将构建一个自定义 React 钩子,通过合并有效负载和去抖 API 调用来捕获...
    程式設計 發佈於2024-11-07
  • 我們走吧!
    我們走吧!
    為什麼你需要嘗試 GO Go 是一種快速、輕量級、靜態類型的編譯語言,非常適合建立高效、可靠的應用程式。它的簡單性和簡潔的語法使其易於學習和使用,特別是對於新手來說。 Go 的突出功能包括內建的 goroutine 並發性、強大的標準庫以及用於程式碼格式化、測試和依賴管理的強大工具...
    程式設計 發佈於2024-11-06
  • 如何將 PNG 圖像編碼為 CSS 資料 URI 的 Base64?
    如何將 PNG 圖像編碼為 CSS 資料 URI 的 Base64?
    在CSS 資料URI 中對PNG 圖像使用Base64 編碼為了使用資料URI 將PNG 圖片嵌入到CSS 樣式表中,PNG資料必須先編碼為Base64 格式。此技術允許將外部圖像檔案直接包含在樣式表中。 Unix 命令列解決方案:base64 -i /path/to/image.png此指令將輸出...
    程式設計 發佈於2024-11-06
  • API 每小時資料的響應式 JavaScript 輪播
    API 每小時資料的響應式 JavaScript 輪播
    I almost mistook an incomplete solution for a finished one and moved on to work on other parts of my weather app! While working on the carousel that w...
    程式設計 發佈於2024-11-06
  • 用於 Web 開發的 PHP 和 JavaScript 之間的主要差異是什麼?
    用於 Web 開發的 PHP 和 JavaScript 之間的主要差異是什麼?
    PHP 與 JavaScript:伺服器端與客戶端 PHP 的作用與 JavaScript 不同。 PHP 運行在伺服器端。伺服器運行應用程式。除此之外,它還處理表單。當您提交表單時,PHP 會對其進行處理。另一方面,JavaScript 是客戶端的。它在瀏覽器中運行。它處理頁面互...
    程式設計 發佈於2024-11-06
  • 如何在 C++ 中迭代結構和類別成員以在運行時存取它們的名稱和值?
    如何在 C++ 中迭代結構和類別成員以在運行時存取它們的名稱和值?
    迭代結構體和類別成員在 C 中,可以迭代結構體或類別的成員來檢索它們的名稱和價值觀。以下是實現此目的的幾種方法:使用巨集REFLECTABLE 巨集可用於定義允許自省的結構。該巨集將結構體的成員定義為以逗號分隔的類型名稱對清單。例如:struct A { REFLECTABLE ( ...
    程式設計 發佈於2024-11-06
  • 如果需要準確答案,請避免浮動和雙精度
    如果需要準確答案,請避免浮動和雙精度
    float 和 double 問題: 專為科學和數學計算而設計,執行二元浮點運算。 不適合貨幣計算或需要精確答案的情況。 無法準確表示10的負次方,例如0.1,從而導致錯誤。 範例1: 減去美元金額時計算錯誤: System.out.println(1.03 - 0.42); // Resu...
    程式設計 發佈於2024-11-06
  • 在 Go 中使用 WebSocket 進行即時通信
    在 Go 中使用 WebSocket 進行即時通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    程式設計 發佈於2024-11-06
  • 如何在 Python 中使用代理程式運行 Selenium Webdriver?
    如何在 Python 中使用代理程式運行 Selenium Webdriver?
    使用Python 中的代理程式執行Selenium Webdriver當您嘗試將Selenium Webdriver 腳本匯出為Python 腳本並從命令列執行時,可能會遇到在使用代理的情況下出現錯誤。本文旨在解決此問題,提供使用代理有效運行腳本的解決方案。 代理整合要使用代理程式來執行 Selen...
    程式設計 發佈於2024-11-06
  • || 什麼時候運算子充當 JavaScript 中的預設運算子?
    || 什麼時候運算子充當 JavaScript 中的預設運算子?
    理解|| 的目的JavaScript 中非布林運算元的運算子在JavaScript 中,||運算子通常稱為邏輯OR 運算符,通常用於計算布林表達式。但是,您可能會遇到 || 的情況。運算符與非布林值一起使用。 在這種情況下,||運算子的行為類似於「預設」運算子。它不傳回布林值,而是根據某些規則傳回左...
    程式設計 發佈於2024-11-06

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3