最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring依賴注入Bean流程深入解析

 更新時間:2026年01月15日 09:11:37   作者:未來龍皇小藍  
本文主要介紹了Spring框架中依賴注入和Bean創(chuàng)建流程,包括注入方式、生命周期、代理模式、注入流程、構造函數(shù)參數(shù)處理以及創(chuàng)建Bean的詳細過程,感興趣的朋友跟隨小編一起看看吧

1-注入方式

常見的方式:

一個類注入另一個類,其實本質都是使用的構造函數(shù),這里以@RequiredArgsConstructor舉例,常見的也有@AllArgsConstructor本質相差無幾

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
    private final UserService userService;
}

@RequiredArgsConstructor:會為所有 final 修飾的字段生成構造函數(shù)參數(shù)

//等價于
@Configuration
public class SecurityConfig {
    private final UserService userService;
    public SecurityConfig(UserService userService) {
        this.userService = userService;
    }
}

優(yōu)點:

  • 使用 final 關鍵字確保依賴不會被意外修改
  • 利用Lombok:減少重復代碼,提高開發(fā)效率

推薦使用 @RequiredArgsConstructor + final 字段

不建議使用:@Autowired、@Resource等方式

2-生命周期

在singleton下:

SecurityConfig Bean 生命周期 == UserService Bean 生命周期
  • 創(chuàng)建時注入
  • 容器不銷毀 → 引用一直有效
  • 不會被替換
  • 不會重新注入

運行時等價于:

// 在存在 AOP(如 @Transactional)時,為代理對象
private final UserService userService = UserService$$Proxy@7a3f21;

3-代理模式

3-1.沒有代理

正常寫業(yè)務

public interface UserService {
    void save();
}
public class UserServiceImpl implements UserService {
    public void save() {
        System.out.println("保存用戶");
    }
}

調用方:

UserService userService = new UserServiceImpl();
userService.save();

3-2.簡單代理

public class UserServiceProxy implements UserService {
    private final UserService target;
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    @Override
    public void save() {
        System.out.println("開啟事務");
        try {
            target.save();
            System.out.println("提交事務");
        } catch (Exception e) {
            System.out.println("回滾事務");
            throw e;
        }
    }
}

使用:

UserService userService = new UserServiceProxy(new UserServiceImpl());
userService.save();

用一個“長得一樣”的對象,包住真正對象,在調用前后加邏輯

有點像包裝類,但是本質不同:

  • 為了給這個對象多加點功能(如加密、壓縮、緩存結果),那是包裝,能訪問到本體,且自己親自放進包裝類中
  • 為了控制權限、延遲加載、單例保證,那是代理,且訪問不到本體
  • 包裝:我?guī)湍愣嘧鳇c事;代理:你只能通過我做這件事

3-3.Spring的代理

Spring的代理 = 自動生成的代理類,Spring只是幫你自動干了這件事

UserService proxy =
    (UserService) Proxy.newProxyInstance(
        loader,
        new Class[]{UserService.class},
        (obj, method, args) -> {
            System.out.println("開啟事務");
            Object result = method.invoke(target, args);
            System.out.println("提交事務");
            return result;
        }
    );

4-注入流程

4-1.概念

【啟動期】
1. 掃描 BeanDefinition
2. 創(chuàng)建原始對象
3. BeanPostProcessor 判斷是否命中 Advisor(如@Transactional相關注解)
4. 命中 → 用代理對象替代原始對象
5. 代理 / 原始對象作為最終 Bean 放入 IOC(singleton)
【運行期】
6. 所有依賴注入,拿到的都是這個最終 Bean
7. 所有 AOP 能力,只存在于代理對象上

只有涉及到增強行為才會創(chuàng)建代理類,比如你在方法上包裹@Transactional,或者你自定義的@Aspect,以及@Async / @Cacheable等等

4-2.示例

@Configuration
public class SecurityConfig {
    private final UserService userService;// Bean 實例(通常是代理對象)的引用,并且在該 Bean 生命周期內長期存在
    public SecurityConfig(UserService userService) {
        this.userService = userService;
    }
    public void test(User user){
        userService.saveData(user);
    }
}
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final UserMapper userMapper;
    @Transactional
    public void saveData(User user){
        userMapper.save(user);
    }
}

這樣的代碼:

  • Spring會首先在IOC容器中,按類型(by type)解析依賴,找到所有符合UserService的Bean,選出一個(或一組),注入(如果存在多個實現(xiàn),會用@Primary@Qualifier,否則直接報錯)
  • 這里因為UserServiceImpl內部掛上了@Transactional注解,所以創(chuàng)建了UserServiceImpl的實例后,就創(chuàng)建一個代理的Bean,即Proxy(UserServiceImpl),后續(xù)的注入都將使用代理的Bean

具體如下:

1. 解析 BeanDefinition(UserServiceImpl)
2. new UserServiceImpl()               ← 原始對象
3. BeanPostProcessor 檢查 @Transactional
4. 命中事務切點
5. 創(chuàng)建代理對象 Proxy(UserServiceImpl)
6. 將代理對象注冊為 UserServiceImpl 的最終 Bean 實例

5-構造函數(shù)的參數(shù)

是Spring在創(chuàng)建Bean時會調用其構造函數(shù),然后根據構造函數(shù)的參數(shù),會進行注入:

@Component
@Slf4j
public class PushDataStrategyManager {
    private final Map<Integer, PushDataStrategy> strategyMap = new HashMap<>();
	// 這里構造函數(shù)的參數(shù)是List<PushDataStrategy> strategies,Spring會找出所有相關的Bean放進來
    public PushDataStrategyManager(List<PushDataStrategy> strategies) {
        // 自動注冊所有策略實現(xiàn)
        for (PushDataStrategy strategy : strategies) {
            strategyMap.put(strategy.getType(), strategy);
            log.info("注冊推送策略: type={}, class={}", strategy.getType(), strategy.getClass().getSimpleName());
        }
    }
}

常見的Spring支持的構造函數(shù)如下,Spring會自動的進行操作:

構造參數(shù)Spring的行為
PushDataStrategy按類型查找Bean;必須唯一,否則報錯
List<PushDataStrategy>查找容器中所有PushDataStrategy類型的Bean,按順序注入
Set<PushDataStrategy>同上,不保證順序
Map<String, PushDataStrategy>key = BeanName,value = Bean 實例

具體流程:

當 Spring 創(chuàng)建 PushDataStrategyManager 時:

  1. 解析構造函數(shù)參數(shù)
public PushDataStrategyManager(List<PushDataStrategy> strategies)
// Spring通過反射拿到泛型信息
// 參數(shù)類型:List
// 泛型參數(shù):PushDataStrategy
  1. 發(fā)現(xiàn)這是一個集合類型依賴
isCollectionType(parameterType)// 是 List
  1. 解析集合的泛型元素類型
ResolvableType.forConstructorParameter(...)
// 得到
elementType = PushDataStrategy.class
  1. 去IOC容器中查找所有候選Bean
// 等價于
applicationContext.getBeansOfType(PushDataStrategy.class)

只要同時滿足下方條件就收集:

  • 是 Bean(@Component / @Service / @Bean
  • 類型是 PushDataStrategy 或其子類
  • 沒被 @Conditional 排除
  • 沒被 @Profile 排除
  1. 排序(如果有順序規(guī)則)
AnnotationAwareOrderComparator.sort(list)
  1. 注入到構造函數(shù)
// 最終等價于:
new PushDataStrategyManager(allStrategies);

6-創(chuàng)建Bean流程

大致的創(chuàng)建流程:

@Component
public class TestStrategy implements PushDataStrategy {}

啟動期-注冊階段:

BeanDefinition:
- beanName = "testStrategy" # BeanName默認為類名首字母小寫
- beanClass = TestStrategy.class
- scope = singleton
- lazy = false
- autowire = constructor
# 此時 還沒有 this 實例地址
# Spring 存的是 BeanName → BeanDefinition(包含 class 信息)

IOC內部結構,Spring 核心容器本質是兩張表:

  1. BeanDefinition Map(啟動期)
Map<String, BeanDefinition>
"testStrategy" -> BeanDefinition(TestStrategy.class)

也可以去直接看代碼,查看啟動期注冊信息,示例:

// Springboot主啟動類:
@SpringBootApplication  // SpringBoot 核心注解,標記這是一個啟動類
@EnableDiscoveryClient  // 開啟 Nacos 注冊發(fā)現(xiàn)
@ComponentScan(basePackages = {"org.myproject.user", "org.myproject.common"})// 公共模塊掃描
public class UserServiceApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(UserServiceApplication.class, args);
        // debug查看beanDefinitionMap
        // 獲取 BeanFactory
        DefaultListableBeanFactory beanFactory =
                (DefaultListableBeanFactory) context.getBeanFactory();
        // 查看所有 BeanDefinition 名稱
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        System.out.println("BeanDefinition 數(shù)量:" + beanNames.length);
        // 注意bean名字是類名的小寫
        BeanDefinition bd = beanFactory.getBeanDefinition("userServiceImpl");
        // 查看某一個 BeanDefinition 的“元信息”
        System.out.println(bd.getBeanClassName());
        System.out.println(bd.getScope());
        System.out.println(bd.isLazyInit());
        // 看到類似:
        //org.myproject.user.service.impl.UserServiceImpl
        //singleton
        //false
    }
}
  1. Singleton Objects Map(運行期)
Map<String, Object>
"testStrategy" -> TestStrategy@5f3a4d

也可以去直接看代碼,查看真正的實例,示例:

@SpringBootApplication  // SpringBoot 核心注解,標記這是一個啟動類
@EnableDiscoveryClient  // 開啟 Nacos 注冊發(fā)現(xiàn)
@ComponentScan(basePackages = {"org.myproject.user", "org.myproject.common"})// 公共模塊掃描
public class UserServiceApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(UserServiceApplication.class, args);
        DefaultListableBeanFactory beanFactory =
                (DefaultListableBeanFactory) context.getBeanFactory();
        // 強轉為可訪問單例池的類型
        DefaultSingletonBeanRegistry registry =
                (DefaultSingletonBeanRegistry) beanFactory;
        // 查看當前已經創(chuàng)建的單例 Bean
        String[] singletonNames = registry.getSingletonNames();
        System.out.println("已創(chuàng)建的 singleton 數(shù)量:" + singletonNames.length);
        // 查看某個 Bean 的真實實例
        Object bean = registry.getSingleton("userServiceImpl");
        System.out.println(bean);
        System.out.println(bean.getClass());
        //已創(chuàng)建的 singleton 數(shù)量:410
        //org.myproject.user.service.impl.UserServiceImpl@6f26e775
        //class org.myproject.user.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$9d7c1b34
        //user使用了事務注解,會創(chuàng)建代理類
        //包含 $$EnhancerBySpringCGLIB$$ 標識
        //這是 Spring CGLIB 動態(tài)代理的典型命名模式
    }
}

7-創(chuàng)建順序

實際創(chuàng)建順序假設:

@Component
public class PushDataStrategyManager {
    public PushDataStrategyManager(List<PushDataStrategy> strategies) {}
}

Spring 執(zhí)行流程:

  1. 需要創(chuàng)建PushDataStrategyManager
→ 發(fā)現(xiàn)構造器參數(shù):
List<PushDataStrategy>
  1. Spring嘗試解析這個參數(shù)
→ 發(fā)現(xiàn)需要:
所有 PushDataStrategy 類型的 Bean
  1. Spring去查容器中已有的BeanDefinition
發(fā)現(xiàn):
testStrategy
emailStrategy
smsStrategy
  1. 如果這些Bean尚未實例化
Spring 會:
先創(chuàng)建它們(遞歸)
  1. 將這些實例組成List
List.of(testStrategy, emailStrategy, smsStrategy)
  1. 調用Manager構造器
new PushDataStrategyManager(list)
  1. Manager實例化完成

總結:

  • Spring不是先“批量創(chuàng)建所有Bean”,而是在創(chuàng)建某個Bean時,遞歸創(chuàng)建它所依賴的Bean
  • 但由于:singleton、non-lazy,看起來像是“啟動時全部創(chuàng)建完”

到此這篇關于Spring依賴注入Bean流程及其理解的文章就介紹到這了,更多相關Spring依賴注入Bean內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java判斷一個時間是否在當前時間區(qū)間代碼示例

    Java判斷一個時間是否在當前時間區(qū)間代碼示例

    這篇文章主要給大家介紹了關于使用Java判斷一個時間是否在當前時間區(qū)間的相關資料,在日常開發(fā)中我們經常會涉及到時間的大小比較或者是判斷某個時間是否在某個時間段內,需要的朋友可以參考下
    2023-07-07
  • Java實現(xiàn)Excel數(shù)據導出的實戰(zhàn)指南

    Java實現(xiàn)Excel數(shù)據導出的實戰(zhàn)指南

    Excel 導出是后臺管理系統(tǒng)中的標配功能,用于將系統(tǒng)數(shù)據導出為 Excel 文件供用戶下載、分析或歸檔,本文提供了一個完整的Excel數(shù)據導出實戰(zhàn)指南,主要針對后臺管理系統(tǒng)中的員工信息導出場景,有需要的小伙伴可以了解下
    2026-06-06
  • MyBatis?Plus如何實現(xiàn)獲取自動生成主鍵值

    MyBatis?Plus如何實現(xiàn)獲取自動生成主鍵值

    這篇文章主要介紹了MyBatis?Plus如何實現(xiàn)獲取自動生成主鍵值問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • java定義受限制的類型參數(shù)操作

    java定義受限制的類型參數(shù)操作

    這篇文章主要介紹了java定義受限制的類型參數(shù)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • SpringBoot 整合 Apache Tika提取數(shù)據的具體操作

    SpringBoot 整合 Apache Tika提取數(shù)據的具體操作

    Apache Tika?是一個功能強大的內容分析工具,它能夠從多種文件格式中提取文本、元數(shù)據以及其他結構化信息,本文介紹了SpringBoot整合ApacheTika進行文件內容提取和數(shù)據泄露防護的實現(xiàn),感興趣的朋友一起看看吧
    2025-02-02
  • Spring?Boot?配置文件類型properties?格式與yml?格式

    Spring?Boot?配置文件類型properties?格式與yml?格式

    這篇文章主要介紹了Spring?Boot?配置文件類型properties?格式與yml?格式,文章圍繞主題展開詳細內容,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • 基于SpringBoot創(chuàng)建Web頁面并熱更新的操作步驟

    基于SpringBoot創(chuàng)建Web頁面并熱更新的操作步驟

    SpringBoot是一個用于快速開發(fā)單個微服務的框架,它基于 Spring 框架,簡化了Spring應用的初始化過程和開發(fā)流程,本文給大家介紹了如何基于SpringBoot創(chuàng)建Web頁面并熱更新,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-11-11
  • Java如何替換第一個和最后一個字符串

    Java如何替換第一個和最后一個字符串

    這篇文章主要介紹了Java如何替換第一個和最后一個字符串的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 如何通過Java代碼實現(xiàn)KMP算法

    如何通過Java代碼實現(xiàn)KMP算法

    這篇文章主要介紹了如何通過Java代碼實現(xiàn)KMP算法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • Java RabbitMQ 中的消息長期不消費會過期嗎

    Java RabbitMQ 中的消息長期不消費會過期嗎

    RabbitMQ支持消息的過期時間,在消息發(fā)送時可以進行指定。 RabbitMQ支持隊列的過期時間,從消息入隊列開始計算,只要超過了隊列的超時時間配置,那么消息會自動的清除
    2021-09-09

最新評論

广安市| 安龙县| 新干县| 秦皇岛市| 巧家县| 灌阳县| 宝兴县| 晋城| 德清县| 厦门市| 东莞市| 习水县| 裕民县| 阿克苏市| 吴忠市| 大兴区| 安塞县| 富民县| 宝坻区| 梁河县| 仁化县| 本溪市| 巍山| 天柱县| 绵竹市| 沂南县| 安远县| 根河市| 库车县| 资溪县| 丘北县| 上栗县| 黑水县| 伊宁市| 胶南市| 班戈县| 图片| 桃江县| 仪陇县| 肃北| 平阳县|