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

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

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

1-注入方式

常見(jiàn)的方式:

一個(gè)類注入另一個(gè)類,其實(shí)本質(zhì)都是使用的構(gòu)造函數(shù),這里以@RequiredArgsConstructor舉例,常見(jiàn)的也有@AllArgsConstructor本質(zhì)相差無(wú)幾

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

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

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

優(yōu)點(diǎn):

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

推薦使用 @RequiredArgsConstructor + final 字段

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

2-生命周期

在singleton下:

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

運(yùn)行時(shí)等價(jià)于:

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

3-代理模式

3-1.沒(méi)有代理

正常寫業(yè)務(wù)

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

調(diào)用方:

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

3-2.簡(jiǎn)單代理

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

使用:

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

用一個(gè)“長(zhǎng)得一樣”的對(duì)象,包住真正對(duì)象,在調(diào)用前后加邏輯

有點(diǎn)像包裝類,但是本質(zhì)不同:

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

3-3.Spring的代理

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

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

4-注入流程

4-1.概念

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

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

4-2.示例

@Configuration
public class SecurityConfig {
    private final UserService userService;// Bean 實(shí)例(通常是代理對(duì)象)的引用,并且在該 Bean 生命周期內(nèi)長(zhǎng)期存在
    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會(huì)首先在IOC容器中,按類型(by type)解析依賴,找到所有符合UserService的Bean,選出一個(gè)(或一組),注入(如果存在多個(gè)實(shí)現(xiàn),會(huì)用@Primary@Qualifier,否則直接報(bào)錯(cuò))
  • 這里因?yàn)閁serServiceImpl內(nèi)部掛上了@Transactional注解,所以創(chuàng)建了UserServiceImpl的實(shí)例后,就創(chuàng)建一個(gè)代理的Bean,即Proxy(UserServiceImpl),后續(xù)的注入都將使用代理的Bean

具體如下:

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

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

是Spring在創(chuàng)建Bean時(shí)會(huì)調(diào)用其構(gòu)造函數(shù),然后根據(jù)構(gòu)造函數(shù)的參數(shù),會(huì)進(jìn)行注入:

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

常見(jiàn)的Spring支持的構(gòu)造函數(shù)如下,Spring會(huì)自動(dòng)的進(jìn)行操作:

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

具體流程:

當(dāng) Spring 創(chuàng)建 PushDataStrategyManager 時(shí):

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

只要同時(shí)滿足下方條件就收集:

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

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

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

@Component
public class TestStrategy implements PushDataStrategy {}

啟動(dòng)期-注冊(cè)階段:

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

IOC內(nèi)部結(jié)構(gòu),Spring 核心容器本質(zhì)是兩張表:

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

也可以去直接看代碼,查看啟動(dòng)期注冊(cè)信息,示例:

// Springboot主啟動(dòng)類:
@SpringBootApplication  // SpringBoot 核心注解,標(biāo)記這是一個(gè)啟動(dòng)類
@EnableDiscoveryClient  // 開(kāi)啟 Nacos 注冊(cè)發(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");
        // 查看某一個(gè) 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(運(yùn)行期)
Map<String, Object>
"testStrategy" -> TestStrategy@5f3a4d

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

@SpringBootApplication  // SpringBoot 核心注解,標(biāo)記這是一個(gè)啟動(dòng)類
@EnableDiscoveryClient  // 開(kāi)啟 Nacos 注冊(cè)發(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();
        // 強(qiáng)轉(zhuǎn)為可訪問(wèn)單例池的類型
        DefaultSingletonBeanRegistry registry =
                (DefaultSingletonBeanRegistry) beanFactory;
        // 查看當(dāng)前已經(jīng)創(chuàng)建的單例 Bean
        String[] singletonNames = registry.getSingletonNames();
        System.out.println("已創(chuàng)建的 singleton 數(shù)量:" + singletonNames.length);
        // 查看某個(gè) Bean 的真實(shí)實(shí)例
        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使用了事務(wù)注解,會(huì)創(chuàng)建代理類
        //包含 $$EnhancerBySpringCGLIB$$ 標(biāo)識(shí)
        //這是 Spring CGLIB 動(dòng)態(tài)代理的典型命名模式
    }
}

7-創(chuàng)建順序

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

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

Spring 執(zhí)行流程:

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

總結(jié):

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

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

相關(guān)文章

  • Java判斷一個(gè)時(shí)間是否在當(dāng)前時(shí)間區(qū)間代碼示例

    Java判斷一個(gè)時(shí)間是否在當(dāng)前時(shí)間區(qū)間代碼示例

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

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

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

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

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

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

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

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

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

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

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

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

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

    Java如何替換第一個(gè)和最后一個(gè)字符串

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

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

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

    Java RabbitMQ 中的消息長(zhǎng)期不消費(fèi)會(huì)過(guò)期嗎

    RabbitMQ支持消息的過(guò)期時(shí)間,在消息發(fā)送時(shí)可以進(jìn)行指定。 RabbitMQ支持隊(duì)列的過(guò)期時(shí)間,從消息入隊(duì)列開(kāi)始計(jì)算,只要超過(guò)了隊(duì)列的超時(shí)時(shí)間配置,那么消息會(huì)自動(dòng)的清除
    2021-09-09

最新評(píng)論

开封县| 商城县| 奎屯市| 洱源县| 亳州市| 武城县| 高雄县| 固阳县| 日照市| 宁陕县| 鄂温| 随州市| 邯郸市| 二连浩特市| 瑞昌市| 南乐县| 夏邑县| 连江县| 郯城县| 宿迁市| 密山市| 杭锦后旗| 广丰县| 双辽市| 苏尼特右旗| 临桂县| 九寨沟县| 冕宁县| 炎陵县| 固镇县| 绍兴市| 新巴尔虎右旗| 钟祥市| 黑河市| 奎屯市| 凌云县| 徐州市| 敦煌市| 酒泉市| 武宁县| 台南县|