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

Springboot Spring原理2深度解析

 更新時(shí)間:2025年11月29日 09:42:39   作者:曾經(jīng)的三心草  
SpringBoot的?動(dòng)配置就是當(dāng)Spring容器啟動(dòng)后, ?些配置類, bean對(duì)象等就自動(dòng)存入到了IoC容器中,不需要我們手動(dòng)去聲明, 從而簡(jiǎn)化了開發(fā), 省去了繁瑣的配置操作,這篇文章介紹Springboot Spring原理2,感興趣的朋友一起看看吧

提示:文章寫完后,目錄可以自動(dòng)生成,如何生成可參考右邊的幫助文檔

前言

3. Spring Boot?動(dòng)配置

自動(dòng)裝配是屬性的自動(dòng)裝配,比如剛剛說的,set注入ben,構(gòu)造方法注入bean
自動(dòng)配置呢

SpringBoot的?動(dòng)配置就是當(dāng)Spring容器啟動(dòng)后, ?些配置類, bean對(duì)象等就自動(dòng)存入到了IoC容器中,不需要我們手動(dòng)去聲明, 從而簡(jiǎn)化了開發(fā), 省去了繁瑣的配置操作.
SpringBoot?動(dòng)配置, 就是指SpringBoot是如何將依賴jar包中的配置類以及Bean加載到Spring IoC容
器中的.
主要分以下兩個(gè)??:

  1. Spring 是如何把對(duì)象加載到SpringIoC容器中的
  2. SpringBoot 是如何實(shí)現(xiàn)的

3.1 Spring 加載Bean

為什么加入依賴,一些bean,注解(@Mapper),就自動(dòng)配置了

比如我們現(xiàn)在加入了一個(gè)包autoconfig,假設(shè)它是第三方的包
如果對(duì)這個(gè)打包,通過pom引入,那么就會(huì)出現(xiàn)在外部庫中

所以這個(gè)我們自定義的第三方的包,放在這里和放在外部庫是一樣的效果
為什么放在這里效果和打包加依賴放在外部庫是一樣的效果呢
因?yàn)閱?dòng)類只能加載它所在目錄下的所有bean,不能加載它所在目錄下的其他包下的bean的
我們的包autoconfig就是放在外面的

@Component
public class CkConfig {
    public void use(){
        System.out.println("CkConfig");
    }
}

然后我們?cè)陧?xiàng)目中就想使用這個(gè)包

@SpringBootApplication
public class SpringPrinciple2Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
    }
}

執(zhí)行

直接報(bào)錯(cuò)了,說找不到這個(gè)bean

因?yàn)閟pring掃描的路徑,默認(rèn)是啟動(dòng)類所在的目錄
但是這個(gè)包在外面,所有加了注解Component也沒有用

第一種方式是使用注解ComponentScan,這個(gè)就是指定要掃描的路徑了

@SpringBootApplication
@ComponentScan("com.ck.demo.autoconfig")
public class SpringPrinciple2Application {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
    }

}

這樣就成功了

就是手動(dòng)指定spring的掃描路徑,但是缺點(diǎn)就是所有的外部的bean都要手動(dòng)指定,比如redis的,mybatis的包都要手動(dòng)加入

而且加了ComponentScan的話,原來的啟動(dòng)類下的路徑就不會(huì)再掃描了,意思就是原來的config下的bean,就是package com.ck.demo.springprinciple2;包下的都不會(huì)再掃描了

ComponentScan下可以寫數(shù)組的

@SpringBootApplication
@ComponentScan({"com.ck.demo.autoconfig","com.ck.demo.springprinciple2"})
public class SpringPrinciple2Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
    }
}

所以這樣之前的,和新加入的都可以生效了

@Import(CkConfig.class)
@SpringBootApplication
public class SpringPrinciple2Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
    }
}

還可以通過這個(gè)方式,使用注解Import,來導(dǎo)入class

但是spring的話,這兩種方式都沒有采用

@Import({CkConfig.class, CkConfig2.class})

也是可以添加多個(gè)的

public class MyImport implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.ck.demo.autoconfig.CkConfig","com.ck.demo.autoconfig.CkConfig2"};
    }
}

我們?cè)诘谌絘utoconfig下寫一個(gè)這個(gè)類,表示Import要掃描的路徑

@Import(MyImport.class)
@SpringBootApplication
public class SpringPrinciple2Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
    }
}

這樣就可以了,這個(gè)就是第三方提供給你bean的路徑

但是還是麻煩,繼續(xù)升級(jí),第三方提供注解就好了

@Target(ElementType.TYPE)//這個(gè)注解可以加在什么地方上
@Retention(RetentionPolicy.RUNTIME)//生命周期
@Import(MyImport.class)//注解的作用
public @interface EnableCkConfig {
}

這個(gè)注解定義在autoconfig下,也是第三方提供的

@EnableCkConfig
@SpringBootApplication
public class SpringPrinciple2Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
        CkConfig2 bean2 = context.getBean(CkConfig2.class);
        bean2.use();
    }
}

這樣就可以了

所以第三方只需要提供注解就可以了

但是導(dǎo)入mybatis的依賴的時(shí)候,就那些bean也不用在啟動(dòng)類上加入注解啊
所以還有其他方式,連注解也不需要加
就是在resource目錄下創(chuàng)建一個(gè)文件,放入"com.ck.demo.autoconfig.CkConfig","com.ck.demo.autoconfig.CkConfig2"這些東西,就會(huì)自動(dòng)創(chuàng)建和管理了

這個(gè)文件就是META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
放入

com.ck.demo.autoconfig.CkConfig
com.ck.demo.autoconfig.CkConfig2

這個(gè)就是告訴spring,我要管理哪些對(duì)象

這樣的話,自定義注解EnableCkConfig也不需要了,也可以獲取第三方的bean了

3.2 源碼分析

但是spring用的是什么方式呢
我們?nèi)タ磫?dòng)類@SpringBootApplication
因?yàn)檫@個(gè)注解,所以它是啟動(dòng)類
還有ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);這個(gè)來啟動(dòng)

點(diǎn)進(jìn)去注解SpringBootApplication

@Target({ElementType.TYPE})表示主要要加在什么上

@Retention(RetentionPolicy.RUNTIME)生命周期

@Documented文檔相關(guān)

@Inherited繼承相關(guān),是源注解,不看了

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

這三個(gè)最重要

@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

因?yàn)檫@個(gè),所以會(huì)掃碼啟動(dòng)類下的路徑
加了這個(gè),默認(rèn)掃描ComponentScan注解所在的路徑,也就是SpringBootApplication注解所在的路徑

SpringBootConfiguration注解點(diǎn)進(jìn)去

Indexed注解是一個(gè)優(yōu)化的注解,不重要

重點(diǎn)是Configuration,五大注解之一,所以SpringBootConfiguration注解就是Configuration分裝了一下
所以注解SpringBootConfiguration表示這是一個(gè)配置類,這個(gè)會(huì)被spring給識(shí)別到

所以重點(diǎn)就是注解EnableAutoConfiguration了

3.3 注解@EnableAutoConfiguration

@Import({AutoConfigurationImportSelector.class})

AutoConfigurationImportSelector這個(gè)實(shí)現(xiàn)了DeferredImportSelector,DeferredImportSelector實(shí)現(xiàn)了ImportSelector

實(shí)現(xiàn)了方法selectImports
返回了要管理的bean

            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

主要是從這個(gè)getAutoConfigurationEntry來的
點(diǎn)進(jìn)去

AnnotationMetadata是注解的原信息

List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

這個(gè)方法點(diǎn)進(jìn)去

這個(gè)就是加載配置了
Assert是斷言

        String aa=null;
        Assert.notNull(aa,"aa不能為空");

比如這個(gè)意思就是斷言aa不能為空
為空的話,就報(bào)"aa不能為空"的錯(cuò)誤

這個(gè)斷言的就是沒有配置信息在META-INF/spring/。。。。。
所以

 ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());

這個(gè)就是讀取META-INF/spring/這個(gè)文件的配置信息的,沒有的話,就會(huì)包一個(gè)異常

但是我們平時(shí)并沒有這個(gè)文件啊,為什么沒有報(bào)錯(cuò)

ctrl+N搜索org.springframework.boot.autoconfigure.AutoConfiguration.imports

點(diǎn)擊第一個(gè)

找到這里,發(fā)現(xiàn)這里就有那個(gè)META-INF/spring/的文件

這里面放的就是spring要加載的bean
說明spring它自己就寫了一個(gè)這種文件了
所以要管理我們第三方的bean的話,也是寫一個(gè)這個(gè)路徑就可以了,spring就會(huì)認(rèn)識(shí)了,spring是根據(jù)路徑來掃描的,都是這個(gè)路徑的就都可以掃描到了

比如這個(gè)里面就有事務(wù)
spring就會(huì)管理DataSourceTransactionManagerAutoConfiguration這個(gè)bean了
ctrl+n搜索

然后DataSourceTransactionManagerAutoConfiguration這個(gè)里面還有bean
這樣就都生效了

比如redis的,搜索RedisAutoConfiguration這個(gè)bean

然后RedisAutoConfiguration這個(gè)bean里面就有RedisTemplate這個(gè)bean,所以RedisTemplate這個(gè)bean也是自動(dòng)注入的

除了@Bean之外,還有其他注解

比如注解ConditionalOnMissingBean
表示在不同的條件下才注入這個(gè)bean

@Bean
@ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }

這個(gè)意思就是,如果你沒有聲明stringRedisTemplate這個(gè)Bean,我就聲明stringRedisTemplate這個(gè)bean

    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )

意思就是你要是聲明了redisTemplate這個(gè)bean,我就不聲明了,你沒有聲明,我就聲明

@ConditionalOnClass({RedisOperations.class})

看有沒有RedisOperations這個(gè)類,沒有的話,這個(gè)類下的所有bean都不,包括自己這個(gè)類的bean都不生效

這個(gè)RedisOperations就在import org.springframework.data.redis.core.RedisOperations;上面擺著
這個(gè)RedisOperations類就是我們要引入的依賴?yán)锩娴?,換個(gè)意思就是沒有引入依賴,就沒有RedisOperations這個(gè)類,就不能加載Bean了

所以加了依賴,外部庫就有org.springframework.data.redis.core,這個(gè)類RedisOperations就有了,對(duì)應(yīng)的RedisAutoConfiguration和它下面的方法這些Bean就有了

不加依賴所以就不行了

繼續(xù)回到這里,就是獲取META-INF/spring/配置的這里
就是

List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

這里configurations就是META-INF/spring/文件里面的配置,包括我們自己寫在META-INF/spring/里面的

this.checkExcludedClasses(configurations, exclusions);

這個(gè)是檢查排除,根據(jù)依賴信息來檢查排除

 configurations.removeAll(exclusions);

這個(gè)就是刪除沒有引入依賴的配置

最后返回

所以getAutoConfigurationEntry返回的就是要求spring給我們管理的配置,對(duì)象,bean

所以注解@Import({AutoConfigurationImportSelector.class}),返回的就是要求spring給我們管理的配置,對(duì)象,bean

現(xiàn)在我們看AutoConfigurationPackage這個(gè)注解

它又import了

import了Register,這又是一個(gè)我們注入包的方式

Register加載bean

public class MyRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(CkConfig.class);
//        beanDefinition.setScope("xxxx");//表示這個(gè)bean是單例的還是多例的
        registry.registerBeanDefinition("ckConfig",beanDefinition);//注入bean,第一個(gè)參數(shù)是bean的名稱
    }
}

我們?cè)赼utoconfig下定義MyRegister

@SpringBootApplication
@Import(MyRegister.class)
public class SpringPrinciple2Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringPrinciple2Application.class, args);
        CkConfig bean = context.getBean(CkConfig.class);
        bean.use();
//        CkConfig2 bean2 = context.getBean(CkConfig2.class);
//        bean2.use();
    }
}

這又是一種方式導(dǎo)入在這里插入代碼片

繼續(xù)分析

這里定義了一個(gè)Register的類
定義了registerBeanDefinitions這個(gè)方法

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])這個(gè)得到的就是我們啟動(dòng)類所在路徑com.ck.demo.springprinciple2

AutoConfigurationPackages.register這個(gè)就是注入第三方的注解,比如@Mapper

為什么spring能夠管理第三方的注解

第三方就會(huì)寫那些register
然后給我們注入

然后BlogInfoMapper使用了注解Mapper
然后注解Mapper就會(huì)拿到BlogInfoMapper的路徑

比如我們搜索這個(gè)MybatisPlus的這個(gè)類

找到這里,這里實(shí)現(xiàn)了Register

這里調(diào)用的registerBeanDefinitions,就是我們剛剛看到的啟動(dòng)類原碼里面定義的方法

就是這個(gè)方法

這里會(huì)得到啟動(dòng)類的路徑
然后在這個(gè)啟動(dòng)類里面掃描得到Mapper注解,掃描誰加了Mapper注解
然后調(diào)用方法registerBeanDefinitions加到spring就可以管理了
這個(gè)就是MybatisPlus第三方注解加入Bean的方式

就是調(diào)用第三方的注解,但是第三方的注解不是立馬加入Bean,而是,先獲取啟動(dòng)類路徑,根據(jù)啟動(dòng)類路徑掃描誰
調(diào)用了Mapper注解,誰調(diào)用個(gè),就調(diào)用方法registerBeanDefinitions加到spring就可以管理了

public class MyRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(CkConfig.class);
//        beanDefinition.setScope("xxxx");//表示這個(gè)bean是單例的還是多例的
        registry.registerBeanDefinition("ckConfig",beanDefinition);//注入bean,第一個(gè)參數(shù)是bean的名稱
    }
}

比如這個(gè),xx使用了注解Mapper(會(huì)自動(dòng)調(diào)用相應(yīng)方法),—》把xx給registry.registerBeanDefinition,然后MyRegister 由于引入了依賴,而且implements ImportBeanDefinitionRegistrar,就自動(dòng)會(huì)被spring給@Import(MyRegister.class)

3.4 總結(jié)

AutoConfigurationPackage用的是Register的方式來注入bean,主要是用來掃描第三方注解,比如@Mapper,
第三方實(shí)現(xiàn)ImportBeanDefinitionRegistrar,然后就可以注入了

當(dāng)SpringBoot程序啟動(dòng)時(shí), 會(huì)加載配置?件當(dāng)中所定義的配置類, 通過 @Import 注解將這些配置類全
部加載到Spring的IOC容器中, 交給IOC容器管理.

  1. Bean的作?域共分為6種: singleton, prototype, request, session, application和websocket.
  2. Bean的?命周期共分為5?部分: 實(shí)例化, 屬性復(fù)制, 初始化, 使?和銷毀
  3. SpringBoot的?動(dòng)配置原理源碼?是 @SpringBootApplication 注解, 這個(gè)注解封裝了3個(gè)注解?@SpringBootConfiguration 標(biāo)志當(dāng)前類為配置類
    ? @ComponentScan 進(jìn)?包掃描(默認(rèn)掃描的是啟動(dòng)類所在的當(dāng)前包及其?包)
    ? @EnableAutoConfiguration
    ? @Import 注解 : 讀取當(dāng)前項(xiàng)?下所有依賴jar包中 META-INF/spring.factories ,
    METAINF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 兩個(gè)?件??定義的配置類(配置類中定義了 @Bean 注解標(biāo)識(shí)的?法)
    ? @AutoConfigurationPackage : 把啟動(dòng)類所在的包下?所有的組件都注?到 Spring容器中

總結(jié)

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

相關(guān)文章

最新評(píng)論

社旗县| 汉川市| 香港 | 大悟县| 山西省| 那曲县| 深水埗区| 西盟| 福鼎市| 吴堡县| 巴马| 秦皇岛市| 肃宁县| 伊通| 泽州县| 沈阳市| 肥东县| 铁力市| 大洼县| 富锦市| 井陉县| 庄浪县| 大厂| 吴堡县| 河池市| 舞阳县| 项城市| 罗田县| 沭阳县| 乌海市| 富蕴县| 平果县| 濮阳县| 武平县| 奇台县| 蓬溪县| 霍山县| 科技| 大邑县| 阜宁县| 大兴区|