優秀的源碼中有着多年沉積下來的精華,這些精華是很是值得咱們學習的。放棄閱讀源碼,你將失去一個和大師學習的機會。html
用Spring框架作了幾年的開發,只停留在會用的階段上,然而Spring的設計思想和原理確實一個巨大的寶庫。大部分人僅僅知道怎麼去配,或着加上什麼屬性就能達到什麼效果,這些東西均可以經過查文檔,查google來解決。關鍵是在怎麼理解它,把它的思想變爲本身的東西。這幾天沒有學習(裝逼),感受內心甚是空虛,索×××了下一直很好奇的Spring大佬,畢竟寫代碼每天都在用。java
前方高能,非戰鬥人員迅速投入戰鬥:web
Spring IOC容器面試
ApplicationContext與BeanFactory探究.spring
bean的加載數據庫
FactoryBean緩存
Spring AOP實現原理及實戰安全
文章篇幅有限,只對Spring幾個重要的知識點進行簡單闡述,有興趣的能夠看看《Spring源碼深度解析》,該書下載地址:https://pan.baidu.com/s/1jGxdGTg,本文也是基於該書總結出來的。微信
不少人一提IOC,便張口就來:控制反轉。究竟哪些方面被反轉了呢?答案是依賴對象的得到被反轉了。不少時候,咱們經過多個對象之間的協做來完成一個功能,若是獲取所依賴對象靠自身來實現,這將致使代碼的耦合度高和難以測試。固然,控制反轉還有一個好聽的名字:依賴注入。session
Spring IOC經過引入xml配置,由IOC容器來管理對象的生命週期,依賴關係等。
從圖中能夠看出,咱們之前獲取兩個有依賴關係的對象,要用set方法,而用容器以後,它們之間的關係就由容器來管理。那麼,Spring容器的加載過程是什麼樣的呢?
BeanDefinition是一個接口,用於屬性承載,好比<bean>元素標籤擁有class、scope、lazy-init等配置。bean的定義方式有千千萬萬種,不管是何種標籤,不管是何種資源定義,不管是何種容器,只要按照Spring的規範編寫xml配置文件,最終的bean定義內部表示都將轉換爲內部的惟一結構:BeanDefinition。當BeanDefinition註冊完畢之後,Spring的BeanFactory就能夠隨時根據須要進行實例化了。
實例化的工做會在容器啓動後過AbstractApplicationContext中reflash方法自動進行。咱們經常使用的ApplicationContext實現類ClassPathXmlApplicationContext繼承了AbstractApplicationContext類,繼承關係以下圖.
AbstractApplicationContext裏的reflash方法是Spring初始IOC容器一個很是重要的方法,無論你是ApplicationContext哪一個實現類,最終都會進入這個方法。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 設置和校驗系統變量和環境變量的值
prepareRefresh();
//主要是建立beanFactory,同時加載配置文件.xml中的beanDefinition
//經過String[] configLocations = getConfigLocations()獲取資源路徑,而後加載beanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//給beanFactory註冊一些標準組建,如ClassLoader,StandardEnvironment,BeanProcess
prepareBeanFactory(beanFactory);
try {
//提供給子類實現一些postProcess的註冊,如AbstractRefreshableWebApplicationContext註冊一些Servlet相關的
//postProcess,真對web進行生命週期管理的Scope,經過registerResolvableDependency()方法註冊指定ServletRequest,HttpSession,WebRequest對象的工廠方法
postProcessBeanFactory(beanFactory);
//調用全部BeanFactoryProcessor的postProcessBeanFactory()方法
invokeBeanFactoryPostProcessors(beanFactory);
//註冊BeanPostProcessor,BeanPostProcessor做用是用於攔截Bean的建立
registerBeanPostProcessors(beanFactory);
//初始化消息Bean
initMessageSource();
//初始化上下文的事件多播組建,ApplicationEvent觸發時由multicaster通知給ApplicationListener
initApplicationEventMulticaster();
//ApplicationContext初始化一些特殊的bean
onRefresh();
//註冊事件監聽器,事件監聽Bean統一註冊到multicaster裏頭,ApplicationEvent事件觸發後會由multicaster廣播
registerListeners();
//非延遲加載的單例Bean實例化
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
代碼邏輯清晰的值得mark一下。這個方法的做用是建立加載Spring容器配置(包括.xml配置,property文件和數據庫模式等)。
BeanFactory體系結構是典型的工廠方法模式,即什麼樣的工廠生產什麼樣的產品。要知道工廠是如何產生對象的,咱們須要看具體的IOC容器實現,具體的實現有:如 DefaultListableBeanFactory 、 XmlBeanFactory 、 ApplicationContext 等。那麼,究竟BeanFactory裏究竟是什麼樣的呢?
package org.springframework.beans.factory;
public interface BeanFactory {
/**
* 用來引用一個實例,或把它和工廠產生的Bean區分開,就是說,若是一個FactoryBean的名字爲a,那麼,&a會獲得那個Factory
*/
String FACTORY_BEAN_PREFIX = "&";
/*
* 四個不一樣形式的getBean方法,獲取實例
*/
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
boolean containsBean(String name); // 是否存在
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否爲單實例
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否爲原型(多實例)
boolean isTypeMatch(String name, Class<?> targetType)
throws NoSuchBeanDefinitionException;// 名稱、類型是否匹配
Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 獲取類型
String[] getAliases(String name);// 根據實例的名字獲取實例的別名
}
咱們能夠看出BeanFactory裏只對IOC容器的基本行爲做了定義,根本不關心你的bean是如何定義怎樣加載的,它規定了全部的容器至少須要實現的標準。說到實現,BeanFactory有幾個比較重要的實現類須要知道,ref:【Spring4揭祕 BeanFactory】基本容器-BeanFactory:https://blog.csdn.net/u011179993/article/details/51636742。那麼BeanFactory的基本實現類XmlBeanFactory與咱們經常使用的ApplicationContext有什麼區別呢?答案是bean的加載。
咱們先看一道面試常常會問到的問題:Spring的bean在何時實例化? ——第一:若是你使用BeanFactory,如XmlBeanFactory做爲Spring Bean的工廠類,則全部的bean都是在第一次使用該bean的時候實例化 。第二:若是你使用ApplicationContext做爲Spring Bean的工廠類,則又分爲如下幾種狀況:
若是bean的scope是singleton的,而且lazy-init爲false(默認是false,因此能夠不用設置),則ApplicationContext啓動的時候就實例化該bean,而且將實例化的bean放在一個線程安全的 ConcurrentHashMap 結構的緩存中,下次再使用該Bean的時候,直接從這個緩存中取 。
若是bean的scope是singleton的,而且lazy-init爲true,則該bean的實例化是在第一次使用該bean的時候進行實例化 。
若是bean的scope是prototype的,則該bean的實例化是在第一次使用該Bean的時候進行實例化 。
ClassPathXmlApplicationContext有幾個重載的構造函數最終都會調用父類AbstractApplicationContext的reflash方法,reflash方法在前文有介紹,做用是建立加載Spring容器配置。AbstractApplicationContext也有getBean方法:
AbstractApplicationContext下的代碼:
public Object getBean(String name) throws BeansException {
//Bean的獲取外部容器交給了內部容器
return getBeanFactory().getBean(name);
}
內部容器由DefaultListableBeanFactory承當,但真實的getBean方法實現是由其父類AbstractBeanFactory實現的,AbstractBeanFactory類一樣實現了BeanFactory接口的方法,它有四個重載的getBean方法,無論哪個都會去調用doGetBean方法:
那麼doGetBean裏幹了什麼事情呢?
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//bean name處理,去除FactoryBean前綴等
final String beanName = transformedBeanName(name);
Object bean = null;
//先從singleton緩存中查看是否已經實例化過該Bean,根據是否有緩存分爲兩個分支分別處理
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 分支一,若緩存中獲取到了而且該BeanDefinition信息代表該bean是singleton的,直接將獲取到的緩存Bean
//(有多是半成品)交給getObjectForBeanInstance處理
/*.........省略logger部分代碼............*/
//調用getObjectForBeanInstance處理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {
// 分之二:沒有緩存,則須要從頭實例化該bean
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);}
// 檢查BeanDefinition是否在當前工廠或父工廠
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// 父工廠getBean
return parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
//將bean加入「正在建立」的集合,完成後會remove,對應afterSingletonCreation/afterPrototypeCreation方法
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 解決依賴關係,將依賴的bean提早實例化
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (int i = 0; i < dependsOn.length; i++) {
String dependsOnBean = dependsOn[i];
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// 這裏又須要根據bean的類型分爲三種狀況:singleton、prototype、request/session
if (mbd.isSingleton()) {
//經過自定義ObjectFactory實例化Bean,此結果多是半成品(是FactoryBean等)
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
//真正實例化裝配的邏輯在createBean方法中
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
//上一步半成品的Bean交給getObjectForBeanInstance方法處理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
//真正實例化裝配的邏輯在createBean方法中
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
//上一步半成品的Bean交給getObjectForBeanInstance方法處理
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
//request、session 的bean
String scopeName = mbd.getScope();
final Scope scope = (Scope) this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
//真正實例化裝配的邏輯在createBean方法中
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
//上一步半成品的Bean交給getObjectForBeanInstance方法處理
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
if (requiredType != null && bean != null &&
!requiredType.isAssignableFrom(bean.getClass())) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return bean;
}
bean的加載經歷了一個複雜的過程,上面代碼主要作了如下幾件事(此段摘抄自《Spring源碼深度解析》):
1.轉換對應的beanName。若是name=「&aa」的,會去除&符號。或者<bean>標籤帶有alias(別名的意思),則取alias所表示最終的beanName。
2.嘗試從緩存中加載單例bean。若是加載不成功,會再次嘗試從singletonFactories中加載。
3.bean的實例化。假如咱們須要對工廠bean進行處理,那麼這裏獲得的實際上是工廠bean 的初始狀態。真正幹活的則是getObjectForBeanInstance定義factory-method方法返回的bean。
4.原型模式的依賴檢查。若是A類有B的屬性,B中有A的屬性,則會產生循環依賴。參考:spring如何解決循環依賴問題http://www.cnblogs.com/bhlsheji/p/5208076.html
5.將存儲的Xml配置文件的GernericBeanDefinition轉換爲RootBeanDefinition。前文提到的用於承載屬性的BeanDefinition有三個實現,GernericBeanDefinition,RootBeanDefinition和ChildBeanDefinition,若是父類bean不爲空的話,這裏會把全部的屬性一併合併父類屬性,由於後續全部的Bean都是針對RootBeanDefinition的。
6.尋找依賴。在初始化一個bean的時候,會首先初始化這個bean所對應的依賴。
7.根據不一樣的scope建立bean。scope屬性默認是singleton,還有prototype、request等。
8.類型轉換。若是bean是個String,而requiredType傳入了Integer,而後返回bean,加載結束。
其中,最重要的步驟是(7),spring的經常使用特性都在那裏實現.
首先要分辨BeanFactory 與 FactoryBean的區別, 兩個名字很像,因此容易搞混。這裏作一個簡單的比喻你就明白了:
1.FactoryBean:工廠類接口,用戶能夠經過實現該接口定製實例化 bean的邏輯。咱們把bean比做是人,那麼FactoryBean則是女媧,首先它自己有人的特徵,但它可以生產人。
2.BeanFactory :BeanFactory定義了 IOC 容器的最基本形式。若是bean還比做是人,那麼它能夠理解成三界,三界裏有各類功能的人,它是一個容器,能夠管理不少的人。
FactoryBean裏幹了什麼事情?
public interface FactoryBean<T> {
//返回由FactoryBean建立的Bean實例,若是isSingleton返回true,則該實例會放到spring容器中單例緩存池中.
T getObject() throws Exception;
//返回FactoryBean建立的bean類型.
Class<?> getObjectType();
//返回由FactoryBean建立的bean實例的做用域是singleton仍是prototype
boolean isSingleton();
}
它的做用不在這裏作闡述,ref:Spring的FactoryBean使用http://www.cnblogs.com/quanyongan/p/4133724.html
寫到這裏,總結一下閱讀Spring源碼的心得:
1.學習Spring思想和編碼規範。Spring的不少函數代碼量大,邏輯複雜,而Spring的編碼風格就是將複雜的邏輯分解,分紅N個小函數的嵌套,每一層都是對下一層的總結和概要。在工做中最佩服的一個大神說過:學習Spring源碼思想爲我所用,哪怕是一天學習一個變量名,他在工做中設計不少小組件的時候都是基於Spring思想和規範。他說,不要迷茫學什麼技術,其實天天只要進步一點點就好,突破的是本身,而不是某個領域。用10年其實才敢說入門一門技術。
2.跟了Spring代碼的函數,你會或多或少發現一些規律:一個真正幹活的函數實際上是以do開頭的,如doGetBean,而給咱們錯覺的函數,如getBean和createBean等等方法,其實只是從全局角度作一些統籌工做。
3.放棄閱讀源碼是一個不明智的選擇,由於你失去了跟大師學習的機會。當你硬着頭皮讀完一個框架的源碼,則其餘框架都是相通的。
4.下一篇文章:Spring AOP是什麼?你都拿它作什麼? 由於篇幅有限,AOP又是一個重要且內容比較多的部分,因此打算單獨拿出來搞事情。
我有一個微信公衆號,常常會分享一些Java技術相關的乾貨;若是你喜歡個人分享,能夠用微信搜索「Java團長」或者「javatuanzhang」關注。