quarkus依賴註入之十:學習和改變bean懶載入規則

来源:https://www.cnblogs.com/bolingcavalry/archive/2023/08/09/17608098.html
-Advertisement-
Play Games

為了降低啟動時間,quarkus下的常規作用域bean遵循懶載入規則,但有時我們希望bean可以更早實例化,本篇,咱們一起來瞭解懶載入規則和改變規則的方法 ...


歡迎訪問我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

  • 本篇是《quarkus依賴註入》系列的第十篇,來看一個容易被忽略的知識點:bean的懶載入,咱們先去瞭解quarkus框架下的懶載入規則,然後更重要的是掌握如何改變規則,以達到提前實例化的目標
  • 總的來說本篇由以下內容構成
  1. 關於懶載入
  2. 編碼體驗懶載入
  3. 改變懶載入規則的第一種手段
  4. 改變懶載入規則的第二種手段(居然和官方資料有出入)
  5. 小結

關於懶載入(Lazy Instantiation

  • CDI規範下的懶載入規則:
  1. 常規作用域的bean(例如ApplicationScoped、RequestScoped),在註入時,實例化的是其代理類,而真實類的實例化發生在bean方法被首次調用的時候
  2. 偽作用域的bean(Dependent和Singleton),在註入時就會實例化
  • quarkus也遵循此規則,接下來編碼驗證

編碼驗證懶載入

  • 為了驗證bean的懶載入,接下來會寫這樣一些代碼
  1. NormalApplicationScoped.java:作用域是ApplicationScoped的bean,其構造方法中列印日誌,帶有自己的類名
  2. NormalSingleton.java:作用域是Singleton的bean,其構造方法中列印日誌,帶有自己的類名
  3. ChangeLazyLogicTest.java:這是個單元測試類,裡面註入了NormalApplicationScoped和NormalSingleton的bean,在其ping方法中依次調用上面兩個bean的方法
  • 以上就是稍後要寫的代碼,咱們根據剛剛提到的懶載入規則預測一下要輸出的內容和順序:
  1. 首先,在ChangeLazyLogicTest的註入點,NormalSingleton會實例化,NormalApplicationScoped的代理類會實例化
  2. 然後,在ChangeLazyLogicTest#ping方法中,由於調用了NormalApplicationScoped的方法,會導致NormalApplicationScoped的實例化
  • 接下來開始寫代碼,第一個bean,NormalApplicationScoped.java
package com.bolingcavalry;

import com.bolingcavalry.service.impl.NormalApplicationScoped;
import com.bolingcavalry.service.impl.NormalSingleton;
import io.quarkus.logging.Log;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;

@QuarkusTest
class ChangeLazyLogicTest {

    @Inject
    NormalSingleton normalSingleton;

    @Inject
    NormalApplicationScoped normalApplicationScoped;

    @Test
    void ping() {
        Log.info("start invoke normalSingleton.ping");
        normalSingleton.ping();
        Log.info("start invoke normalApplicationScoped.ping");
        normalApplicationScoped.ping();
    }
}
  • 第二個bean,NormalSingleton.java
package com.bolingcavalry.service.impl;

import io.quarkus.logging.Log;
import javax.inject.Singleton;

@Singleton
public class NormalSingleton {

    public NormalSingleton() {
        Log.info("Construction from " + this.getClass().getSimpleName());
    }

    public String ping() {
        return "ping from NormalSingleton";
    }
}
  • 然後是單元測試類ChangeLazyLogicTest,可見NormalApplicationScoped構造方法的日誌應該在start invoke normalApplicationScoped.ping這一段之後
package com.bolingcavalry;

import com.bolingcavalry.service.impl.NormalApplicationScoped;
import com.bolingcavalry.service.impl.NormalSingleton;
import io.quarkus.logging.Log;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;

@QuarkusTest
class ChangeLazyLogicTest {

    @Inject
    NormalSingleton normalSingleton;

    @Inject
    NormalApplicationScoped normalApplicationScoped;

    @Test
    void ping() {
        Log.info("start invoke normalSingleton.ping");
        normalSingleton.ping();
        Log.info("start invoke normalApplicationScoped.ping");
        normalApplicationScoped.ping();
    }
}
  • 編碼完成,運行單元測試類,驗證我們之前的預測,控制台輸出結果如下圖所示,符合預期

image-20220429184753022

  • 至此,懶載入基本規則咱們已經清楚了,聰明的您應該想到了此規則的弊端:如果在構造方法中有一些耗時操作,必須等到第一次調用bean的方法時才會執行,這可能不符合我們的預期,有時候我們希望應用初始化的時候把耗時的事情做完,這樣執行bean方法的時候就沒有影響了
  • 顯然,quarkus也意識到了這個問題,於是,給出了兩中改變懶載入規則的方法,使得bean的實例化可以更早完成,接下來咱們逐個嘗試

改變懶載入規則的第一種手段

  • 讓bean儘早實例化的第一種手段,是讓bean消費StartupEvent事件,這是quarkus框架啟動成功後發出的事件,從時間上來看,此事件的時間比註入bean的時間還要早,這樣消費事件的bean就會實例化

  • 咱們給NormalApplicationScoped增加下圖紅框中的代碼,讓它消費StartupEvent事件

image-20220501093358565
  • 運行代碼前,先預測一下修改後的結果
  1. 首先應該是NormalApplicationScoped的實例化
  2. NormalApplicationScoped實例收到StarttupEvent事件,列印日誌
  3. 開始註入bean到ChangeLazyLogicTest,引發NormalApplicationScoped代理類和NormalSingleton的實例化
  4. 簡單地說:原本最晚實例化的NormalApplicationScoped,由於消費StarttupEvent事件,現在變成了最早實例化的
  • 現在運行代碼驗證,如下圖,符合預期
image-20220501094806401

改變懶載入規則的第二種手段(居然和官方資料有出入)

  • 第二種方法更簡單了:用StartupEvent修飾類,下圖是完整NormalApplicationScoped代碼,可見改動僅有紅框位置
image-20220501101416574
  • 在運行代碼前,先預測一下運行結果,理論上應該和第一種手段的結果差不多:NormalApplicationScoped、NormalApplicationScoped代理、NormalSingleton,
  • 上述推測的依據來自Startup源碼中的註釋,如下圖,官方表示StartupEvent和Startup效果一致
image-20220501102631368
  • 官方都這麼說了,我豈敢不信,不過流程還是要完成的,把修改後的代碼再運行一遍,截個圖貼到文中,走走過場...

  • 然而,這次運行的結果,卻讓人精神一振,StartupEvent和Startup效果是不一樣的!!!

  • 運行結果如下圖,最先實例化的居然不是被Startup註解修飾的NormalApplicationScoped,而是它的代理類!

image-20220501102150488
  • 由此可見,Startup可以將bean的實例化提前,而且是連帶bean的代理類的實例化也提前了
  • 回想一下,雖然結果與預期不符合,而預期來自官方註釋,但這並不代表官方註釋有錯,人家只說了句functionally equivalent,從字面上看並不涉及代理類的實例化
  • 另外Startup也有自己的獨特之處,一共有以下兩點
  1. Startup註解的value屬性值,是bean的優先順序,這樣,多個bean都使用Startup的時候,可以通過value值設置優先順序,以此控制實例化順序(實際上控制的是事件observer的創建順序)
  2. 如果一個類只有Startup註解修飾,而沒有設置作用域的時候,quarkus自動將其作用域設置為ApplicationScoped,也就是說,下麵這段代碼中,ApplicationScoped註解寫不寫都一樣
@ApplicationScoped
@Startup
public class NormalApplicationScoped {

小結

  • 懶載入、StartupEvent、Startup這三種情況下的實例化順序各不相同,最好是有個對比讓大家一目瞭然,方便選擇使用
  • 接下來就畫個對比圖,圖中有懶載入、StartupEvent、Startup三個場景,每個場景都是三個階段:quarkus框架初始化、註入bean、bean的方法被調用,每個階段都有哪些對象被實例化就是它們最大的區別,如下所示
流程圖 (3)
  • 至此,懶載入相關的知識點學習完畢,個人認為這是個很重要的技能,用好了它對業務有不小的助力,希望能給您一些參考吧

歡迎關註博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • MySQL 和 Elasticsearch 是兩種不同的數據管理系統,它們各有優劣,適用於不同的場景。本文將從以下幾個方面對它們進行比較和分析: - 數據模型 - 查詢語言 - 索引和搜索 - 分散式和高可用 - 性能和擴展性 - 使用場景 ## 數據模型 MySQL 是一個關係型資料庫管理系統(R ...
  • 本篇文章主要是對方案性能優化2.0中,所做的緩存設計的過程、方案、結果做一個總結。 一、前言 對於方案中心,核心業務場景之一是物流場景下的物流費用計算。而部分業務場景下,對於物流費用計算的性能有較高要求,如ICBU網站運費模板鏈路,通方案中心計算快遞、海拼物流費用。在接入新的流量場景的背景下(ICB ...
  • 作為開發公司,我們開發完APP,如何發給客戶下載測試呢?安卓APP可以通過QQ直接發送給客戶,客戶可以在QQ內直接點擊安裝。 但是現在很多客戶都不用QQ,用微信的居多。而通過微信直接發送安卓APP安裝包(apk)的話,是不可以像QQ那樣直接點擊安裝的。這就需要我們把APP生成二維碼提供給客戶下載安裝 ...
  • > 本文首發於[掘金](https://juejin.cn/post/7264128388288708664),未經許可禁止轉載 Vuex4 是 Vue 的狀態管理工具,Vuex 和單純的全局對象有以下兩點不同: 1. Vuex 的狀態存儲是響應式的 2. 不能直接改變 store 中的狀態。改變 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 我們經常會遇到一個場景,比如在一個列表中批量獲取用戶的信息。 如果我們一次性往後端發送幾十條請求是非常愚蠢的事情。此時我們就要學會如何使用批量獲取的邏輯。 但是批量獲取有一個問題就是,我需要在用戶列表項的上層去獲取,然後再把結果分發給下層 ...
  • 分享的 WebStorm 2023.2 最新激活註冊碼,可免費永久激活,親測有效,下麵是詳細文檔哦~ 申明:本教程 WebStorm 激活碼收集於網路,請勿商用,僅供個人學習使用,如有侵權,請聯繫作者刪除。若條件允許,希望大家購買正版 ! PS: 本教程最新更新時間: 2023年08月08日~ 前言 ...
  • 最近,群友分享了一個很有意思的效果: ![](https://img2023.cnblogs.com/blog/608782/202308/608782-20230808101320920-311621134.gif) 原效果的網址:[frosted-glass](https://frosted-g ...
  • 說到 Hybrid App(混合應用)大家都不陌生,因為這種開發模式大行其道發展的這些年取代了很多原生和 Web 應用,為什麼大家對這種「Native + HTML5」的開發模式額外偏愛呢? 因為一方面在一定程度上兼顧了原生應用的優質體驗,另一方面又兼顧到了 HTML5 靈活的開發模式。 這種模式的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...