我們?cè)?a href="http://www.reibang.com/p/8cdc86aeee53" target="_blank">《spring源碼閱讀2-1——bean的管理》中,摸清了BeanFactory家族的整體框架和功能概覽兴猩,本文將繼續(xù)剖析spring如何將BeanFactory一點(diǎn)一點(diǎn)實(shí)現(xiàn)的浅缸。
首先是上次已經(jīng)獲得的BeanFactory家族類(lèi)圖卖漫,本人在這上方加了備注柑船,希望能讓大家看的更為清晰澎怒。
SimpleAliasRegistry
這個(gè)類(lèi)實(shí)現(xiàn)了AliasRegistry接口
首先是registerAlias()
方法的實(shí)現(xiàn):
為了方便讀者閱讀杭抠,加入了中文注釋
源碼:
private final Map<String, String> aliasMap = new ConcurrentHashMap<String, String>(16);
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty"); //參數(shù)校驗(yàn)
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) { //保障了不存在與正名相同的別名
this.aliasMap.remove(alias);
}
else {
if (!allowAliasOverriding()) { //是否開(kāi)啟覆蓋注冊(cè)脸甘,默認(rèn)為true
String registeredName = this.aliasMap.get(alias);
if (registeredName != null && !registeredName.equals(name)) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
checkForAliasCircle(name, alias); //確保添加的沒(méi)有name和alias值相反的數(shù)據(jù)且alias和name不相等
this.aliasMap.put(alias, name);
}
}
---------------------------- 方法封裝 ------------------------------------------------------
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
protected void checkForAliasCircle(String name, String alias) {
if (alias.equals(canonicalName(name))) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
解讀:
第一句是聲明了一個(gè)線程安全的散列表對(duì)象(ConcurrentHashMap)來(lái)作為緩存(如果對(duì)線程安全和非線程安全的集合類(lèi)不太清楚,建議可以系統(tǒng)地去學(xué)習(xí))來(lái)存放注冊(cè)的alias偏灿。
aliasMap以alias為鍵斤程,name為值,其中alias為別名菩混,而name是正名忿墅,如下圖所示。
從代碼中我們看到我們可以為把a(bǔ)lias當(dāng)做name來(lái)再次注冊(cè)別名沮峡,舉個(gè)例子疚脐,叫"張三"的人有兩個(gè)外號(hào):"小張"和"小三",那我可以為小三注冊(cè)別名邢疙,叫"三哥"棍弄,即name="小三", alias="三哥"(當(dāng)然name="張三"也是可以的)。但是不能注冊(cè)name和alias正好相反的數(shù)據(jù)疟游,以及name和alias相等的數(shù)據(jù)呼畸。為什么這么設(shè)計(jì)呢?后面會(huì)給出解釋颁虐。
源碼:
@Override
public void removeAlias(String alias) {
String name = this.aliasMap.remove(alias);
if (name == null) {
throw new IllegalStateException("No alias '" + alias + "' registered");
}
}
@Override
public boolean isAlias(String name) {
return this.aliasMap.containsKey(name);
}
@Override
public String[] getAliases(String name) {
List<String> result = new ArrayList<String>();
synchronized (this.aliasMap) {
retrieveAliases(name, result);
}
return StringUtils.toStringArray(result);
}
//遍歷散列表蛮原,獲取name為定值的所有alias
private void retrieveAliases(String name, List<String> result) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String alias = entry.getKey();
result.add(alias);
retrieveAliases(alias, result); //注意是有回調(diào)的哦
}
}
}
解讀:
removeAlias()
和isAlias()
就不說(shuō)了,根據(jù)name來(lái)獲取alias的方法getAliases(String name)
另绩,由于散列表是以alias為鍵儒陨,而name為值,要想找出滿足條件的所有alias值笋籽,就必須對(duì)散列表進(jìn)行遍歷蹦漠,找出所有name和給定值相等的值。
我在注釋中注明了是有回調(diào)函數(shù)存在的车海,當(dāng)找到name值和給定值相等是笛园,會(huì)把這個(gè)alias作為name,再次對(duì)散列表進(jìn)行遍歷,如下圖所示
返回值中有"三哥"研铆,而三哥對(duì)應(yīng)的name是"小三"埋同。這也是在注冊(cè)時(shí)限制注冊(cè)的數(shù)據(jù)name和alias不能正好相反且name和alias不能相等的原因,否則回調(diào)函數(shù)將陷入無(wú)限循環(huán)中去蚜印。
因此莺禁,在設(shè)計(jì)階段和編程過(guò)程中,如果涉及回調(diào)窄赋,就要規(guī)避無(wú)限循環(huán)的可能哟冬。
至此,我們已經(jīng)閱讀完了這個(gè)類(lèi)的所有方法(還有一個(gè)resolveAliases()入?yún)⑹且粋€(gè)較為復(fù)雜的對(duì)象忆绰,暫時(shí)不解釋?zhuān)认禄嘏龅搅嗽偌?xì)究)
DefautlSingletonBeanRegistry
該類(lèi)繼承了SimpleAliasRegistry浩峡,并且實(shí)現(xiàn)SingletonBeanRegistry接口。
同樣的错敢,來(lái)看下是如何實(shí)現(xiàn)registerSingleton(String beanName, Object singletonObject)
方法:
源碼:
protected static final Object NULL_OBJECT = new Object();
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
addSingleton(beanName, singletonObject);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
解讀:
同SimpleAliasRegistry翰灾,這里也是用散列表來(lái)做緩存。但是SingleBean的注冊(cè)要來(lái)的復(fù)雜稚茅,因?yàn)閎ean還涉及到初始化的問(wèn)題纸淮,因此這里有多個(gè)緩存用的對(duì)象:
- singletonObjects是一個(gè)線程安全的散列表,用于存放已注冊(cè)的SingleBean實(shí)例
- singletonFactories用來(lái)存放初始化SingleBean實(shí)例的factory對(duì)象
- earlySingletonObjects 用來(lái)存放已存在但是未注冊(cè)的SingleBean實(shí)例
- registeredSingletons是一個(gè)集合亚享,存放的是已注冊(cè)的SingleBean對(duì)象的名稱
從代碼中可以看到咽块,當(dāng)一個(gè)Bean被注冊(cè)后,就會(huì)將這個(gè)Bean實(shí)例添加到緩存中去欺税,如果這個(gè)實(shí)例存在對(duì)應(yīng)的BeanFactory侈沪,則BeanFactory將被移除,或者這個(gè)Bean存在于earlySingletonObjects中晚凿,也將從earlySingletonObjects中移除亭罪。
這個(gè)過(guò)程可能不太好理解,舉個(gè)例子來(lái)說(shuō)明吧:
有a歼秽,b应役,c三個(gè)學(xué)生要在大學(xué)注冊(cè),a社會(huì)自考招生哲银,沒(méi)有學(xué)籍扛吞;b是高中FactoryB的學(xué)生;c是該大學(xué)的留級(jí)生荆责,學(xué)籍已存在。b注冊(cè)完成后亚脆,要將學(xué)籍檔案轉(zhuǎn)移到大學(xué)中做院,c也要將學(xué)籍檔案轉(zhuǎn)移到當(dāng)前年級(jí)的檔案庫(kù)中去(如下圖所示)
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
@Override
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
解讀:
這段邏輯比較清晰了,還是那上面那個(gè)例子來(lái)講,要獲得一個(gè)學(xué)生的學(xué)籍檔案键耕,首先直接從該年級(jí)的學(xué)籍檔案中查看寺滚,如果沒(méi)有,這個(gè)學(xué)生是否為該入學(xué)注冊(cè)的學(xué)生(isSingletonCurrentlyIncreation()
方法判斷)屈雄,如果是村视,就依次查找earlySingletonObjects和singletonFactories對(duì)象。
再看源碼的過(guò)程中酒奶,會(huì)有很多疑惑蚁孔,比如這里為什么要設(shè)置這么多的緩存對(duì)象,想alias那樣注冊(cè)不就好了惋嚎,為什么要那么麻煩呢杠氢?但是,要沉住氣另伍,堅(jiān)持下去鼻百,最終會(huì)讓你豁然開(kāi)朗。這也許是源碼閱讀的魅力所在吧
源碼:
@Override
public boolean containsSingleton(String beanName) {
return this.singletonObjects.containsKey(beanName);
}
@Override
public String[] getSingletonNames() {
synchronized (this.singletonObjects) {
return StringUtils.toStringArray(this.registeredSingletons);
}
}
@Override
public int getSingletonCount() {
synchronized (this.singletonObjects) {
return this.registeredSingletons.size();
}
}
這幾個(gè)方法的重寫(xiě)就不說(shuō)了摆尝,和alias的注冊(cè)管理是一樣的温艇。
源碼:
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
public void registerDependentBean(String beanName, String dependentBeanName) {
// A quick check for an existing entry upfront, avoiding synchronization...
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
return;
}
// No entry yet -> fully synchronized manipulation of the dependentBeans Set
synchronized (this.dependentBeanMap) {
dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
dependentBeans = new LinkedHashSet<String>(8);
this.dependentBeanMap.put(canonicalName, dependentBeans);
}
dependentBeans.add(dependentBeanName);
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean == null) {
dependenciesForBean = new LinkedHashSet<String>(8);
this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
}
dependenciesForBean.add(canonicalName);
}
}
protected boolean isDependent(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
return false;
}
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
for (String transitiveDependency : dependentBeans) {
if (isDependent(transitiveDependency, dependentBeanName)) {
return true;
}
}
return false;
}
protected boolean hasDependentBean(String beanName) {
return this.dependentBeanMap.containsKey(beanName);
}
public String[] getDependentBeans(String beanName) {
Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
if (dependentBeans == null) {
return new String[0];
}
return StringUtils.toStringArray(dependentBeans);
}
解讀:
DefaultSingletonBeanRegistry不僅實(shí)現(xiàn)了SingleBeanRegistry中的對(duì)SingleBean注冊(cè)管理的方法,還擴(kuò)展了對(duì)于SingleBean的依賴關(guān)系進(jìn)行管理堕汞。
緩存對(duì)象:
- dependentBeanMap 存儲(chǔ)bean所依賴的其他bean組件
- dependenciesForBeanMap 存儲(chǔ)當(dāng)前bean被哪些其他bean組件依賴
方法:
- registerDependentBean() ==> 注冊(cè)bean所依賴的dependentBean
- isDependent() ==> 返回denpendentBean是否為bean的依賴bean
- hasDenpendentBean() ==> 返回bean是否有dependentBean
- getDependentBeans() ==> 返回bean的所有dependentBean
- destroyBean() ==> 刪除一個(gè)bean勺爱,刪除其依賴信息
總結(jié)
本文到這就要結(jié)束了,回顧下
本文主要對(duì)Factory家族中的SimpleAliasRegistry和DefaultSingletonBeanRegistry進(jìn)行分析臼朗。
其主要通過(guò)線程安全的散列表做緩存邻寿,實(shí)現(xiàn)了alias管理、SingleBean管理以及Bean依賴的管理的功能视哑。
本文到此就結(jié)束绣否,希望能夠幫到大家。
純屬原創(chuàng)挡毅,轉(zhuǎn)載請(qǐng)聲明出處蒜撮。
——作者:陳來(lái)件(QQ:810522442)