Spring IoC源碼分析

這是一篇純源碼的文章厨姚,可能有些枯燥,希望大家喜歡。不廢話


一、源碼編譯

1.下載:

git clone https://github.com/spring-projects/spring-framework.git 1

cd spring-framework/

2.編譯:

先配置gradle編譯環(huán)境

gradle -v

idea中配置gradle

3.直接導(dǎo)入idea钻弄,在Terminal中執(zhí)行如下命令,編譯Spring源碼

gradlew spring-oxm:compileTestJava

4.正常情況是不會(huì)確實(shí)cglib包的卖局,如果確實(shí),在Terminal中執(zhí)行

gradle objenesisRepackJar gradle cglibRepackJar

會(huì)在Spring-framework\spring-core\build\libs生成缺失的jar

二双霍、IoC容器設(shè)計(jì)理念

IoC也稱為依賴注入dependency injection, DI)砚偶。它是一個(gè)對(duì)象定義依賴關(guān)系的過程,也就是 說洒闸,對(duì)象只通過構(gòu)造函數(shù)參數(shù)染坯、工廠方法的參數(shù)或?qū)ο髮?shí)例構(gòu)造或從工廠方法返回后在對(duì)象實(shí) 例上設(shè)置的屬性來定義它們所使用的其他對(duì)象。然后容器在創(chuàng)建bean時(shí)注入這些依賴項(xiàng)丘逸。這 個(gè)過程基本上是bean的逆過程单鹿,因此稱為控制反轉(zhuǎn)(IoC)

在Spring中,構(gòu)成應(yīng)用程序主干并由Spring IoC容器管理的對(duì)象稱為bean深纲。bean是由Spring IoC容器實(shí)例化仲锄、組裝和管理的對(duì)象。

IoC容器設(shè)計(jì)理念:通過容器統(tǒng)一對(duì)象的構(gòu)建方式湃鹊,并且自動(dòng)維護(hù)對(duì)象的依賴關(guān)系儒喊。

三、Spring IoC的使用及其原理分析

1.bean的裝配方式 xml

ApplicationContext context = new

ClassPathXmlApplicationContext("spring.xml");

實(shí)現(xiàn)FactoryBean

public class MyFactroyBean implements FactoryBean {

@Override

public Object getObject() throws Exception {

return new ReentrantLock();

}

@Override

public Class getObjectType() {

return ReentrantLock.class;

}

}

思考:FactoryBean和BeanFactory的區(qū)別币呵?

AbstractBeanFactory#getObjectForBeanInstance

!(beanInstance instanceof FactoryBean) ||

BeanFactoryUtils.isFactoryDereference(name)

ObjectFactory 和FactoryBean的區(qū)別怀愧?

@Component +@ComponentScan @Component , @Repository,@Service , @Controller @Component 是通用注解余赢,其他三個(gè)注解是這個(gè)注解的拓展芯义,并且具有了特定的功能 @Repository 注解在持久層中,具有將數(shù)據(jù)庫操作拋出的原生異常翻譯轉(zhuǎn)化為spring的持 久層異常的功能妻柒。 @Service 層是業(yè)務(wù)邏輯層注解扛拨,這個(gè)注解只是標(biāo)注該類處于業(yè)務(wù)邏輯層。 @Controller 層是spring-mvc的注解举塔,具有將請求進(jìn)行轉(zhuǎn)發(fā)鬼癣,重定向的功能陶贼。

@ComponentScan("bat.ke.qq.com")

@Configuration

public class AppConfig {

}

AnnotationConfigApplicationContext context = new

AnnotationConfigApplicationContext(AppConfig.class)

思考: @Configuration和@Component區(qū)別?

@Configuration 實(shí)現(xiàn)了@Component待秃,還有其他作用

@Bean+ @Configuration

@Configuration

public class AppConfig {

@Bean

public User user(){

return new User();

}

}

思考:配置 @Configuration和不配置的區(qū)別拜秧? 不配置@Configuration: 當(dāng)內(nèi)部method bean發(fā)生彼此依賴的時(shí)候會(huì)導(dǎo)致多例 @Configuration: 1.將@Configuration配置的Appconfig由普通類型轉(zhuǎn)變?yōu)閏glib代理類型 2.將AppConfig的beanDefinitioin屬性賦值為full類型的(不配置的是lite) @Configuration源碼分析: ConfigurationClassPostProcessor#postProcessBeanFactory

AbstractApplicationContext#invokeBeanFactoryPostProcessors

>PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(Configu

rableListableBeanFactory, BeanFactoryPostProcessor>)

>ConfigurationClassPostProcessor#postProcessBeanFactory

>> enhanceConfigurationClasses(beanFactory); //增強(qiáng)配置類 cglib

// 轉(zhuǎn)換為cglib類型

>>Class enhancedClass = enhancer.enhance(configClass,

this.beanClassLoader);

@Import

@ComponentScan("bat.ke.qq.com")

@Configuration

@Import(value = MyImportBeanDefinitionRegistrar.class)

public class AppConfig {

@Bean

public User user(){

return new User();

}

}

@Import(AppConfig2.class)

ImportSelector

public class MyImportSelector implements ImportSelector {

@Override

public String[] selectImports(AnnotationMetadata

importingClassMetadata)

return new String[]{Fox.class.getName()};// 只能根據(jù)byType

}

}

ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements

ImportBeanDefinitionRegistrar {

@Override

public void registerBeanDefinitions(AnnotationMetadata

importingClassMetadata,BeanDefinitionRegistry registry) {

//創(chuàng)建BeanDefinition

RootBeanDefinition rootBeanDefinition = new

RootBeanDefinition(Fox.class);

// 注冊到容器

registry.registerBeanDefinition("fox",rootBeanDefinition);

}

}

@ImportResource("spring.xml")

@Conditional

@ComponentScan("bat.ke.qq.com")

@Configuration

public class AppConfig {

@Bean

public Cat cat(){

return new Cat();

}

@Bean

@Conditional(value = MyConditional.class)

public Fox fox(){

return new Fox()

}

}

public class MyConditional implements Condition {

@Override

public boolean matches(ConditionContext context, AnnotatedTypeMetadata

metadata) {

if(context.getBeanFactory().containsBean("cat"))

return true;

return false;

}

}

2.bean的注冊原理

思考:bean的屬性是什么對(duì)象承載的? bean是如何注冊到容器中的章郁?

BeanDefinition (Bean定義) 承載bean的屬性信息

BeanDefinitionRegistry(Bean注冊器) bean的id作為key注冊 beanName

AliasRegistry (別名注冊器) bean的name作為key注冊

BeanDefinitionHolder 包裝 BeanDefinition id name(多個(gè))

BeanDefinitionReader(Bean定義讀韧鞯) 讀取spring配置文件

BeanDefinitionParser bean定義解析器 parser解析 schema

ConfigurationClassParser 配置類解析器

BeanMethod @bean修飾的方法bean

ConfigurationClass 配置類 緩存BeanMethod到 beanMethods

Beanfactory (bean工廠) 生產(chǎn)bean

xml配置

//創(chuàng)建一個(gè)簡單注冊器

//BeanDefinitionRegistry register = new SimpleBeanDefinitionRegistry();

//創(chuàng)建一個(gè)實(shí)現(xiàn)了注冊器的工廠

BeanDefinitionRegistry registry = new DefaultListableBeanFactory();

//創(chuàng)建bean定義讀取器

BeanDefinitionReader reader = new XmlBeanDefinitionReader(register);

// 創(chuàng)建資源讀取器

//DefaultResourceLoader resourceLoader = new DefaultResourceLoader();

// 獲取資源

//Resource xmlResource = resourceLoader.getResource("spring.xml");

reader.loadBeanDefinitions("spring.xml");

// 裝載Bean的定義

reader.loadBeanDefinitions(xmlResource);

// 打印構(gòu)建的Bean 名稱

System.out.println(Arrays.toString(register.getBeanDefinitionNames());

// 工廠調(diào)用getBean方法

System.out.println(regis解析xml并注冊beanter.getBean("user"));

源碼分析

AbstractApplicationContext#refresh

>>obtainFreshBeanFactory();

>>refreshBeanFactory();

>>loadBeanDefinitions(beanFactory);

>>loadBeanDefinitions(beanDefinitionReader);

>XmlBeanDefinitionReader#doLoadBeanDefinitions

>DefaultBeanDefinitionDocumentReaderarseBeanDefinitions

// 解析xml bean標(biāo)簽

>BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)

// 生成beanName(id 和name) 和beanDefinition

>BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element,

BeanDefinition)

>DefaultBeanDefinitionDocumentReader#processBeanDefinition

>>BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,

getReaderContext().getRegistry());

>BeanDefinitionReaderUtils#registerBeanDefinition

>> registry.registerBeanDefinition(beanName,

definitionHolder.getBeanDefinition());

// 注冊beanDefiniton

>DefaultListableBeanFactory#registerBeanDefinition

>>this.beanDefinitionMap.put(beanName, beanDefinition);

>>this.beanDefinitionNames.add(beanName);

>>this.manualSingletonNames.remove(beanName);

java Configuration

AnnotationConfigApplicationContext context =

new AnnotationConfigApplicationContext(AppConfig.class);

// 通過容器獲取到beanFactory 即是工廠,又是注冊器

DefaultListableBeanFactory factory =

context.getDefaultListableBeanFactory();

RootBeanDefinition beanDefinition = new RootBeanDefinition(Fox.class);

factory.registerBeanDefinition("fox",beanDefinition);

//beanDefinition.setAutowireMode(2);

//spring 填充屬性

beanDefinition.getPropertyValues().add("name","foxxx");

ConfigurationClassPostProcessor#processConfigBeanDefinitions ConfigurationClassParser#processConfigurationClass ConfigurationClassParser#doProcessConfigurationClass

gApplicationContext context = new

AnnotationConfigApplicationContext(AppConfig.class);

eregister(annotatedClasses);

>AnnotatedBeanDefinitionReader#registerBean(Class)

// 注冊appConfig 到容器

>AnnotatedBeanDefinitionReader#doRegisterBean

// 容器啟動(dòng)

tBean("fox"));

源碼分析

AbstractApplicationContext#refresh

>>invokeBeanFactoryPostProcessors(beanFactory);

>PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcess

ors

>ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

>ConfigurationClassPostProcessor#processConfigBeanDefinitions

>>ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,

this.metadataReaderFactory) // 設(shè)置配置類屬性是full還是lite

>> parser.parse(candidates); // 解析配置類

>ConfigurationClassParser#parse(Set)

>ConfigurationClassParser#parse(AnnotationMetadata, String)

>> doProcessConfigurationClass(confi// 配置類解析器

gClass, sourceClass);

>ConfigurationClassParser#doProcessConfigurationCla 將@Component修飾的bean

注冊到容器ss

// 解析@ComponentScan

>> this.componentScanParser.parse(componentScan,

sourceClass.getMetadata().getClassName());

>>>ComponentScanAnnotationParser#parse

// basePackages屬性修飾的

>>> ClassPathBeanDefinitionScanner#doScan

// 找到被@Component修飾的類

>>>> findCandidateCoponents(base Package);

>>>> registerBeanDefinition(definitionHolder, this.registry);

// 處理@Import

>> ConfigurationClassParser#processImports

// im 并不會(huì)將bean注冊到容器plements ImportSelector

>>> selector.selectImports(currentSourceClass.getMetadata());

// 處理@Bean ConfigurationClass 緩存beanMethods

>>>ConfigurationClass#addBeanMethod

// 回到processConfigBeanDefinitions

>ConfigurationClassPostProcessor#processConfigBeanDefinitions

// 注冊bean到容器

>>> this.reader.loadBeanDefinitions(configClasses);

>>ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurati

onClass

// 注冊 實(shí)現(xiàn)了ImportSelector的bean

>>> registerBeanDefinitionForImportedConfigurationClass(configClass);

// 方法bean 注冊到容器

>>> loadBeanDefinitionsForBeanMethod(beanMethod);

// @ImportResource("spring.xml") 配置的bean注冊到容器

loadBeanDefinitionsFromImportedResources(configClass.getImportedResources(

));

// 實(shí)現(xiàn) ImportBeanDefinitionRegistrar的 bean 注冊到容器

>>> loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionR

egistrars());

3.bean的依賴注入原理

查找方式:

byType

byName

注入方式: field

反射的方式暖庄,field.set(obj,value)

constructor

不配置 @Autowired情況下

多構(gòu)造器時(shí)聊替,有無參構(gòu)造器,則調(diào)用無參構(gòu)造器培廓,若沒有無參構(gòu)造器惹悄,當(dāng)剩余構(gòu)造器大于 1個(gè)時(shí),拋異常肩钠,當(dāng)只有一個(gè)時(shí)泣港,會(huì)執(zhí)行此構(gòu)造器

當(dāng)只有一個(gè)有參構(gòu)造器時(shí),會(huì)執(zhí)行此構(gòu)造器

當(dāng)該bean的beanDefinition設(shè)置了AutowireMode為3后价匠,則會(huì)選擇構(gòu)造器貪婪模式当纱,執(zhí)行參數(shù)最多的構(gòu)造器(前提:構(gòu)造參數(shù)類型都是bean類型)

@Component

public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory

beanFactory) throws BeansException {

AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)

beanFactory.getBeanDefinition("userServiceImpl");

beanDefinition.setAutowireMode(3);//構(gòu)造器貪婪模式

}

}

setter方法

不配置 @Autowired情況下 通過設(shè)置AutowireMode為1或者2,會(huì)調(diào)用setter方法踩窖,通過setter方法注入bean

AutowireCapableBeanFactory類

//沒有外部定義的自動(dòng)裝配

int AUTOWIRE_NO = 0;

//基于規(guī)范的setter方法名調(diào)用坡氯,比如setUserDao(UserDao userDao)

//---setUserDao---bat.ke.qq.com.dao.UserDao@64cd705f

int AUTOWIRE_BY_NAME = 1;

/**

*基于setter方法的參數(shù)類型調(diào)用,set開頭即可,比如

* ---setUserDao---bat.ke.qq.com.dao.UserDao@63355449

* ---setUserDaoxxxx---bat.ke.qq.com.dao.UserDao@63355449

* ---setxxxxxxUserDao---bat.ke.qq.com.dao.UserDao@63355449

**/

int AUTOWIRE_BY_TYPE = 2;

//自動(dòng)裝配最貪婪的構(gòu)造函數(shù)

int AUTOWIRE_CONSTRUCTOR = 3;

今天就寫到這里了洋腮,太累了箫柳。頭發(fā)又少了幾根,啃了一陣子spring源碼啥供,今天才敢寫一點(diǎn)東西滞时,希望對(duì)大家有幫助,最少也能理清一下滤灯。

創(chuàng)作不易坪稽,各位的支持和認(rèn)可,就是我創(chuàng)作的最大動(dòng)力鳞骤,我們下篇文章見窒百!

下篇文章我準(zhǔn)備整理一篇《Bean的生命周期》不知道大家是否感興趣,也可以評(píng)論告訴我豫尽。

如果本篇有任何錯(cuò)誤篙梢,請批評(píng)指教,不勝感激 美旧!

聯(lián)系我/公眾號(hào)(java耕耘者)一個(gè)每天吃飯睡覺打豆豆寫代碼的人渤滞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贬墩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子妄呕,更是在濱河造成了極大的恐慌陶舞,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绪励,死亡現(xiàn)場離奇詭異肿孵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)疏魏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門停做,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人大莫,你說我怎么就攤上這事蛉腌。” “怎么了只厘?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵烙丛,是天一觀的道長。 經(jīng)常有香客問我懈凹,道長蜀变,這世上最難降的妖魔是什么悄谐? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任介评,我火速辦了婚禮,結(jié)果婚禮上爬舰,老公的妹妹穿的比我還像新娘们陆。我一直安慰自己,他們只是感情好情屹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布坪仇。 她就那樣靜靜地躺著,像睡著了一般垃你。 火紅的嫁衣襯著肌膚如雪椅文。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天惜颇,我揣著相機(jī)與錄音皆刺,去河邊找鬼。 笑死凌摄,一個(gè)胖子當(dāng)著我的面吹牛羡蛾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锨亏,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痴怨,長吁一口氣:“原來是場噩夢啊……” “哼忙干!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浪藻,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤捐迫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后珠移,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弓乙,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年钧惧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暇韧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浓瞪,死狀恐怖懈玻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乾颁,我是刑警寧澤涂乌,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站英岭,受9級(jí)特大地震影響湾盒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诅妹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一罚勾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吭狡,春花似錦玫氢、人聲如沸训裆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烙无。三九已至瘦陈,卻和暖如春踪区,著一層夾襖步出監(jiān)牢的瞬間铺韧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工蟹略, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留登失,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓科乎,卻偏偏與公主長得像壁畸,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容