總結對SpringIOC的理解
一.IOC定義
- IOC是控制翻轉,程序提供依賴關係給Spring,由Spring來維護依賴關係
- 類的產生過程交由Spring容器
- 將產生的實例對象,通過DI注入
IOC是目標,DI依賴注入是手段
IOC思想是: 一個實例的產生就不應該由程序員自己new出來,而是如果你需要代理類,就創建一個代理類對象傳給你;如果你僅僅需要類的實例對象,那麼就創建一個對象注入給你
二.預備概念
1.beanFactory和factoryBean區別
- beanFactory是Spring的bean工廠,用來產生bean的
- factoryBean本身是一個Bean,是由Spring管理的對象;當一個類依賴關係很複雜時,可以使用factoryBean,不用自己來維護依賴關係。
比如:Mybatis提供了sqlSessionFactoryBean對象,該對象可以返回給Spring一個sqlSessionFactory對象,Spring拿到該對象可以直接創建會話,至於依賴關係,由Mybatis維護好。
2.beanDefinition
- beanDefinition與Bean的關係就好比java中的類與class字節碼的關係
- Java中的類是對象,使用class類來描述(類名、類的方法、類的成員變量等)
- Bean也是Java中的對象,只不過Bean比類更豐富,它不僅有常規屬性,還有別的屬性(單例、懶加載)等,這些特點class無法描述。所以使用BeanDefinition把Bean的屬性抽象出來
3.BeanFactoryPostProcessor後置處理器
- 後置處理器,是Spring的擴展點之一;
- 在任意Bean被實例化之前,程序會執行程序員自定義(實現了該接口的類)的後置處理器和Spring中自帶的後置處理器
- 後置處理器的作用是:可以按照自己需求在bean創建之前定義bean屬性、創建之後初始化、自定義注入方式等
比如:aop實現,就是通過後置處理器(下一篇會介紹)
再比如:DI依賴注入,Spring默認是@AutoWired通過java反射注入類的元數據。可以通過自定義類實現後置處理器接口,修改自動注入屬性模型,採用構造方法注入或setter()注入。
再比如:bean的生命週期的過程,在9個地方使用了5種不同的後置處理器,來處理對象實例化前後過程以及實例的屬性設置
三.IOC源碼分析
1.構造Spring容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)
-
三種創建Spring容器的方法
AnnotationConfigApplicationContext
ClassXmlApplicationContext
FileSystemXmlApplicationContext -
Spring容器的理解
從微觀上:spring容器即存放beanDefinition的beanDefinitionMap容器(本質是ConcurrentHashMap)
從宏觀上:即整個Spring環境(類似你要建造一個廠子,首先需要選址落地,容器即整個大環境)(大環境裏面包括:工廠beanFactory用來產生Bean、Reader解析器將類抽象成beanDefinition、Registry註冊器將beanDefinition put至beanDefinitionMap中、BeanPostProcessor後置處理器對Spring進行擴展)
2.配置Spring容器環境-讀取器、註冊器、原材料
2.1-this()構造方法中
new AnnotatedBeanDeflinitionReader()
- 使用AnnotatedBeanDeflinition來修飾被加了@直接的BeanDeflinition;
- Reader是beanDefinition讀取器:給定一個Java類,通過Reader可以將其抽象爲BeandDefinition對象
2.2-register()
- Java類被Reader抽象爲BeanDefinition後,會被BeanDifinitionRegistry的registry()註冊(map.put())至beanDefinitionMap中去。
- 被put至BeanDefinitionMap中的BeanFefinition對象,就是後續Spring工廠BeanFactory加工(創建=實例化)對象的原材料。
3.初始化Spring環境
refresh()
-
通過1.之後,Spring已經創建了BeanFactory工廠框架,裏面有基本的beanDefinitionMap等
-
通過2.之後,工程BeanFactory已經包含beanDefinition了,此時可以準備初始化工廠了
3.1 通知BeanFactory使用上下文類加載器
3.2 爲工廠添加後置處理器
- 兩種後置處理器:程序員實現接口自定義的、Spring中自帶的;
- 其中SpringAop等過程在此期間完成
- 執行完後置處理器後,此時beanDefinitionMap中已經有7個beanDefinition對象。其中就有我們的啓動類AppConfig
- 接下來Spring會遍歷這7個beanDefinition,解析他們是否加了@註解;如果加了註解,判斷加了什麼註解
3.3啓動類AppConfig註解@解析
- 判斷這7個beanDefinition是否加了下面四個註解:@Configuration、@ComponentScan、@Component、@Import
- 如果加了Configuration註解,則不用再去判斷是否加了其他三個註解,直接執行
doScan()方法
- 通過scan掃描basePackage路徑下的我們書寫的.java文件(XXXController、ServiceImp等),將符合條件的類通過Reader讀取器抽象爲BeanDefinition
- 將這些beanDefinition對象放入set中
- 等到所有掃描完成後,會統一遍歷set集合中的beanDefinition對象,查看他們的基本屬性以及是否添加了下面註解:@Component、@Controller、@Service、@Repository。如果加了這些註解,則將這些beanDefinition對象,從set中取出來,通過Registry註冊(put)至BeanDefinitionMap中。
3.4 實例化對象-Spring中Bean的生命週期
工廠環境搭建好了,生產實例化對象的原材料beanDefinition也全部放入map中了,接下來實例化單例對象
- 第一處後置處理器:解析aop切面信息,然後將切面類抽象的beanDefinition對象放入單例池map中緩存
- 第二處後置處理器:通過反射調用選出的構造方法(此處選出構造方法過程很複雜)創建對象,放入SingleFactory中
- 第三處後置處理器:通過構造方法方式,注入實例對象
- 第四處後置處理器:處理循環引用
public class ServiceImp{
@AutoWired
private Dao dao;
}
public class DaoImp{
@AutoWired
private Service service;
}
- 創建serviceImp實例對象後,先將其放入SingleFactory中;
- 發現serviceImp中有一個Dao屬性,通過ByName或者ByType方式找到Dao類型,調用getBean(dao)
- 同理實例化DaoImp後,將其放入SingleFactory中,也同樣發現了有一個Service屬性,通過ByName或ByType方式找到S而vice類型,調用getBean(service)
- 執行上述getBean(service)後,會去SingleFactory中拿Service實例;首先將Service實例對象從SingleFactory中刪除,放入SingleObject中;然後通過反射的方式,獲取該service實例的mateData元數據;最後通過Field.set(dao,service)將service實例對象信息注入給dao對象
- 此時dao實例對象創建結束;再執行Field.set(service,dao)將dao實例對象的元數據信息注入給service實例對象,至此完成了循環引用問題
5…第五處後置處理器:判斷bean是否需要完成屬性填充
6.第六處後置處理器:完成bean屬性填充=>依賴注入
7.第七處後置處理器:即bean實例初始化之前,前置處理器
8.第八處後置處理器:即bean實例初始化之後,後置處理器
9.第九處後置處理器:destory銷燬Bean實例
3.5注入實例化對象
經過3.4已經實例化對象了,準備採用DI依賴注入對象
首先Spring中的依賴注入分爲三種:構造方法、setter方法、Java反射方式
- Spring默認方式是採用的@Autowired通過Java反射注入
- 可以自定義後置處理器,不採用@Autowired方式,而是通過setter()方法方式注入;
- 在bean還未實例化之前,通過implement BeanfactoryPostProcessor接口,將Bean注入模型修改爲setter(),這樣可以在注入是完全擺脫@AutoWired,減少對Spring的依賴
public class MyBeanPostProcessor implements BeanFactoryProcessor{
xxx;
setAutoWiredMode(2);//默認是2
//@Autowired默認值爲0;
//構造方法:1
//setXxx()方法:2
}
- 在實例化對象A後,發現對象中依賴類B
- 通過ByName或ByType找到依賴對象類型
- ByName缺點是:
<bean id=“dao1” class=“DaoImp”
<bean id=“dao2” class=“DaoImp”
A類實例對象去找依賴時,從map中找到了兩個Dao類型的實例對象,A對象不知道使用哪一個,會報錯
- ByType:根據構造方法SetXxx(),將set去掉,然後將構造方法名第一個大寫字母小寫,即:xxx來找類型
3… 找到依賴對象類型後,調用getBean(B)方法;從SingleFactory中拿到該類型實例對象
4… 通過反射方式獲取該實例類的元數據信息
5… 通過Field.set(A,B)方法,將B實例對象通過Spring默認的@AutoWired Java反射方式注入到A實例對象中;至此,SpringIOC控制翻轉完成~