一、簡介
?? 感謝“簡易java框架”分享的學習心得。循著他的足跡,我把picocontainer讀了一遍。源代碼的版本是1.2-RC-2。
?? pico的官方站點:http://www.picocontainer.org/
?? 由于它是一個專門的ioc容器,所以使用起來沒有spring那么麻煩。關(guān)于他的文檔,在官方站點上有一篇《5分鐘搞定pico》的文章。國人似乎也有很多的翻譯版本。講解得很詳細,大家可以看看。
二、快速入手
?? 先來體驗一下pico的最簡單的用法。
????
public
?
static
?
void
?main(String[]?args)?
{
????????MutablePicoContainer?pico
=
?
new
?DefaultPicoContainer(
new
?SetterInjectionComponentAdapterFactory());

????????pico.registerComponentImplementation(User.
class
,User.
class
,
new
?Parameter[]
{
new
?ConstantParameter(
new
?String

(
"
lvhb
"
)),
new
?ConstantParameter(
new
?String(
"
boy
"
))}
);
????????pico.registerComponentInstance(String.
class
,
"
namea
"
);
????????
????????pico.registerComponentImplementation(LifeUser.
class
);
//
????????new?VerifyingVisitor().traverse(pico);
//
????????pico.start();
????????????????
????????LifeUser?user
=
(LifeUser)pico.getComponentInstance(LifeUser.
class
);
????????user.start();
?}
?在LifeUser的start方法中,我這樣寫到:
? public void start() {
? // TODO Auto-generated method stub
? System.out.println("lifeuser start");
? System.out.println(user.getName());
?}
?我們構(gòu)造了2個類,一個是User類,一個是LifeUser類。由于pico有管理生命周期的功能,我們把LifeUser繼承自Startable.
?User類有個2個屬性.name和sex.
?LifeUser有3個屬性.name,age和User,這里安排一個User是為了觀察它的依賴注入的過程.
三、結(jié)構(gòu)分析
?下圖是PicoContainer的體系結(jié)構(gòu)。

?外部需要的api都有MutablePicoContainer提供.他提供了注冊/取消實現(xiàn),注冊/取消實例和設(shè)置父子關(guān)系的操作.
?在DefaultPicoContainer中的componentKeyToAdapterCache屬性用來存儲注冊過的各種類或者實例.
?下圖是ComponentAdapter的體系結(jié)構(gòu)

?ComponentAdapter是pico功能實現(xiàn)的主體.
?我們知道在DefaultPicoContainer我們是用一個hashmap存儲的key->value的鍵值對。
?其中的key就是我們注冊的接口,如果沒有提供接口,容器就用這個實現(xiàn)類作key.比如在上面的例子中,
? pico.registerComponentImplementation(LifeUser.class);我們也可以要LifeUser繼承自ILifeUser,然后寫成這樣
?? pico.registerComponentImplementation(ILifeUser.class,LifeUser.class);
?value就是我們要介紹的ComponentAdapter.
?在ComponentAdapter接口中,提供了5個方法,分別是:
?getComponentKey()獲得自己在PicoContainer里面的key
?getComponentImplementation()獲得自己的實現(xiàn)類
?getComponentInstance(PicoContainer container)生成自己的一個實例
?verify(PicoContainer container)檢驗這個ComponentAdapter的依賴性是否完整。
?accept(PicoVisitor visitor)
?ComponentAdapter接口的兒孫很多,但是我們知道,流行的依賴注入目前有2中形式:構(gòu)造注入和設(shè)值注入。
?因此盡管實現(xiàn)或者繼承ComponentAdapter的各種類很多,最后用到的必將是2個類。
?SetterInjectionComponentAdapter和ConstructorInjectionComponentAdapter。有哪些中間的ComponentAdapter實現(xiàn)呢?
?來看看ComponentAdapter得層次:
?? 首先有個抽象類MonitoringComponentAdapter繼承他,在目前的版本中,monitor功能并沒有真正實現(xiàn)。
?? 抽象類AbstractComponentAdapter繼承自MonitoringComponentAdapter。在他的構(gòu)造函數(shù)里完成對componentKey和componentImplementation的保存。?并對componentImplementation和componentKey是否兼容作判斷。
?抽象類InstantiatingComponentAdapter繼承自AbstractComponentAdapter。他檢查傳入的componentImplementation是否能夠被實例化,并保存了注冊時傳入的參數(shù),是否允許沒有public構(gòu)造方法,以及生命周期策略。值得注意的是?這里聲明了一個抽象類Guard,用來在實現(xiàn)不同的注入方式時作回調(diào)函數(shù)。
?最終的SetterInjectionComponentAdapter和ConstructorInjectionComponentAdapter都繼承自InstantiatingComponentAdapter
?
?在ComponentAdapter的兒孫中有個CachingComponentAdapter,他繼承自DecoratingComponentAdapter,實現(xiàn)了LifecycleManager。?顧名思義,他實現(xiàn)了我們的cache.即提供單實例模式。并提供了生命周期管理的功能。
?這里使用了修飾模式。CachingComponentAdapter是SetterInjectionComponentAdapter和ConstructorInjectionComponentAdapter的修飾類。
?在這個類的內(nèi)部,額外實現(xiàn)了單實例。并對生命周期相關(guān)的過程作校驗處理。
?他實現(xiàn)單實例的方法比較有意思.
?if (instanceReference.get() == null) {
??????????? Object instance = super.getComponentInstance(container);
??????????? instanceReference.set(instance);
??????? }
四、調(diào)試跟蹤--注冊。
?ok,看看運行的流程。
?我們先來測試默認的用法:
?????
public
?
static
?
void
?main(String[]?args)?
{
????????MutablePicoContainer?pico
=
?
new
?DefaultPicoContainer();

????????pico.registerComponentImplementation(User.
class
,User.
class
,
new
?Parameter[]
{
new
?ConstantParameter(
new
?String

(
"
lvhb
"
)),
new
?ConstantParameter(
new
?String(
"
boy
"
))}
);
????????pico.registerComponentInstance(String.
class
,
"
namea
"
);
????????
????????pico.registerComponentImplementation(LifeUser.
class
);
//
????????new?VerifyingVisitor().traverse(pico);
????????pico.start();
????????
????????
????????LifeUser?user
=
(LifeUser)pico.getComponentInstance(LifeUser.
class
);
??????
//
??user.start();
?}
?這段代碼將輸出start里面的內(nèi)容.
?lifeuser start
? lvhb
? ----
?讓我們來debug一下上面提到的測試例子.
?1.調(diào)用DefaultPicoContainer(ComponentAdapterFactory componentAdapterFactory,LifecycleStrategy
lifecycleStrategyForInstanceRegistrations,PicoContainer parent)
?? parent可以為null,其他2個如果沒有設(shè)置,將用默認的DefaultComponentAdapterFactory和new DefaultLifecycleStrategy(new DefaultComponentMonitor()).
?2.注冊User類.調(diào)用registerComponentImplementation,并傳遞2各參數(shù).先調(diào)用pico初始化設(shè)置的componentAdapterFactory生成componentAdapter
?ComponentAdapter componentAdapter =componentAdapterFactory.createComponentAdapter(componentKey, componentImplementation, parameters).
?我們進入createComponentAdapter的方法體:
?return new CachingComponentAdapter(new ConstructorInjectionComponentAdapter(componentKey, componentImplementation, parameters, false, currentMonitor(), lifecycleStrategy));
?這里的monitor和lifecycleStrategy在容器初始化componentAdapterFactory的時候已經(jīng)設(shè)置.我們在下面的內(nèi)容將忽略關(guān)于監(jiān)視器和生命周期管理的內(nèi)容.
?這里用了ConstructorInjectionComponentAdapter,并交給CachingComponentAdapter修飾.
?在里面的內(nèi)容上面已經(jīng)介紹了,ConstructorInjectionComponentAdapter一級一級的向上傳遞參數(shù),一層扒一層皮.完成各種檢查.
?在包裝后的CachingComponentAdapter上調(diào)用registerComponent(componentAdapter),即把它加到pico的hashmap中.
?3.相同的原理完成String和LifeUser的注冊.
五.調(diào)試跟蹤--獲得實例
?1.獲得adapter.
??? 調(diào)用pico的getComponentInstance(Object componentKey)方法.下面是DefaultPicoContainer的方法體:
???
?
public
?Object?getComponentInstance(Object?componentKey)?
{
????????ComponentAdapter?componentAdapter?
=
?getComponentAdapter(componentKey);

????????
if
?(componentAdapter?
!=
?
null
)?
{
????????????
return
?getInstance(componentAdapter);

????????}
?
else
?
{
????????????
return
?
null
;
????????}
????}
??? getComponentAdapter(componentKey)將會在pico的hashmap中查找對應(yīng)的componentKey,如果找不到,在父容器里面找.
??? 獲得componentAdapter后,調(diào)用getInstance(componentAdapter);方法.因為有可能是在父類中找到的adapter.所以做了一定的判斷.
? 2.通過adapter產(chǎn)生實例
??? 最終調(diào)用instance = componentAdapter.getComponentInstance(this);方法.
??? 由于ComponentAdapter的實現(xiàn)是個ConstructorInjectionComponentAdapter,我們來看他的這個方法.
?? 這里有最重要的2個方法,內(nèi)部類Guard的run()和ComponentAdapter的getGreediestSatisfiableConstructor(PicoContainer container).????
??? 進入getComponentInstance后,首先構(gòu)造了一個抽象類Guard的匿名內(nèi)部類給instantiationGuard,在這個類的run方法里面:
????? a:調(diào)用getGreediestSatisfiableConstructor方法獲得最佳的構(gòu)造函數(shù).
????? b:調(diào)用getConstructorArguments獲得所需的參數(shù)
????? c:調(diào)用newInstance(constructor, parameters)生成實例.
??? 我們把剛才的instantiationGuard賦上所需的參數(shù),然后調(diào)用他的observe方法.該方法:
??????? public final Object observe(Class stackFrame) {
??????? if (Boolean.TRUE.equals(get())) {
??????????? throw new CyclicDependencyException(stackFrame);
??????? }
??????? Object result = null;
??????? try {
??????????? set(Boolean.TRUE);
??????????? result = run();
??????? } catch (final CyclicDependencyException e) {
??????????? e.push(stackFrame);
??????????? throw e;
??????? } finally {
??????????? set(Boolean.FALSE);
??????? }
??????? return result;
??? }
??? 調(diào)用了我們設(shè)置的run()并返回結(jié)果.
???
? 六.獲得構(gòu)造函數(shù)并傳遞參數(shù)的具體過程
??? 這里要說的即上面獲得實例過程中run方法的實現(xiàn)細節(jié).
???? 1.調(diào)用getGreediestSatisfiableConstructor方法獲得最佳的構(gòu)造函數(shù).
???? a.首先調(diào)用getSortedMatchingConstructors方法初步篩選構(gòu)造函數(shù).
?
???
for
?(
int
?i?
=
?
0
;?i?
<
?allConstructors.length;?i
++
)?
{
????????????Constructor?constructor?
=
?allConstructors[i];
????????????
if
?((parameters?
==
?
null
?
||
?constructor.getParameterTypes().length?
==
?parameters.length)?
&&
?(allowNonPublicClasses?


||
?(constructor.getModifiers()?
&
?Modifier.PUBLIC)?
!=
?
0
))?
{
????????????????matchingConstructors.add(constructor);
????????????}
????????}
? 上面這段意思是說如果注冊時沒有提供參數(shù),把所有構(gòu)造函數(shù)列為候選,如果有提供參數(shù),選取和提供參數(shù)個數(shù)相同的構(gòu)造函數(shù)作為候選.排除私有構(gòu)造函數(shù).用一個ArrayList保存所有的候選構(gòu)造函數(shù).如果注冊時候沒有配參數(shù),那么按構(gòu)造參數(shù)從多到少排列.存放在sortedMatchingConstructors中.
?? b.然后遍歷每一個構(gòu)造函數(shù),檢查是否是我們所需.
??? 我們來看這幾行代碼:
?????????
???Class[]?parameterTypes?
=
?constructor.getParameterTypes();
????????????Parameter[]?currentParameters?
=
?parameters?
!=
?
null
?
?
?parameters?:?createDefaultParameters(parameterTypes);

????????????
//
?remember:?all?constructors?with?less?arguments?than?the?given?parameters?are?filtered?out?already
????????????
for
?(
int
?j?
=
?
0
;?j?
<
?currentParameters.length;?j
++
)?
{
????????????????
//
?check?wether?this?constructor?is?statisfiable
????????????????
if
?(currentParameters[j].isResolvable(container,?
this
,?parameterTypes[j]))?
{
????????????????????
continue
;
????????????????}
????????????????unsatisfiableDependencyTypes.add(Arrays.asList(parameterTypes));
????????????????unsatisfiedDependencyType?
=
?parameterTypes[j];
????????????????failedDependency?
=
?
true
;
????????????????
break
;
????????????}
??? c.檢查的關(guān)鍵是這段currentParameters[j].isResolvable(container, this, parameterTypes[j])
???? 說到這里,我們先看看他的Parameter體系
??????
?
??? parameters是我們在注冊過程中構(gòu)造componentAdapter時保存的.(User.class有參數(shù),String和LifeUser都沒有).
??? 從UML圖上看到,parameter有3種,我們這里著重介紹ConstantParameter和ComponentParameter
??? ConstantParameter用來包裝常量性質(zhì)的參數(shù),比如本文提供的例子中的參數(shù).
??? 來看他isResolvable方法:
??? 這個方法應(yīng)該是檢查注冊時的每個參數(shù)和構(gòu)造函數(shù)的每個參數(shù)是否匹配,我們進去看看.
??? 他的!checkPrimitive(expectedType) && !expectedType.isInstance(value)表達式前半句對基本類型作了處理.后半句判斷注冊的參數(shù)的值是否是構(gòu)造函數(shù)對應(yīng)參數(shù)類型兼容.
??? ComponentParameter用來包裝組件(自定義類)類型的參數(shù).他的isResolvable和ConstantParameter是不同的.
??? 下面略去500字.(getTargetAdapter,? List found = container.getComponentAdaptersOfType(expectedType);)
???
??? d.如果提供的注冊參數(shù)都是構(gòu)造函數(shù)的依賴.那么failedDependency=false.我們把這個構(gòu)造函數(shù)先保存下來
???? greediestConstructor = constructor;
??? lastSatisfiableConstructorSize = parameterTypes.length;
????????????
?? 接著去檢查下一個構(gòu)造函數(shù)是否所需.
?? 顯然,如果注冊時設(shè)置了參數(shù),那么parameterTypes就會是個固定值,因為在上面的篩選中都是選擇的相同的參數(shù)個數(shù)的構(gòu)造方法.如果出現(xiàn)另外一個可以匹配的構(gòu)造函數(shù),而且參數(shù)個數(shù)相同的情況,說明存在沖突.
?? 我們來考慮parameters==null的情況.這個時候返回的是所有的構(gòu)造函數(shù).而且它的currentParameters =createDefaultParameters(parameterTypes).
?? createDefaultParameters方法是InstantiatingComponentAdapter實現(xiàn)的.代碼如下:
??
?
protected
?Parameter[]?createDefaultParameters(Class[]?parameters)?
{
????????Parameter[]?componentParameters?
=
?
new
?Parameter[parameters.length];

????????
for
?(
int
?i?
=
?
0
;?i?
<
?parameters.length;?i
++
)?
{
????????????componentParameters[i]?
=
?ComponentParameter.DEFAULT;
????????}
????????
return
?componentParameters;
????}
? 顯然,他默認設(shè)置了ComponentParameter作為參數(shù).每個參數(shù)都是一個new ComponentParameter().參照c節(jié)介紹的判斷方法.
如果構(gòu)造函數(shù)的每個參數(shù)都能找到依賴,(因為是參數(shù)從大到小排列),那么它就應(yīng)該是最合適的構(gòu)造函數(shù).
? e.ok,我們找到了最好的構(gòu)造函數(shù)了.
?? 現(xiàn)在需要給這個函數(shù)找到各個參數(shù)的值.
???? Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
??? for (int i = 0; i < currentParameters.length; i++) {
??????????? result[i] = currentParameters[i].resolveInstance(container, this, parameterTypes[i]);
??????? }
??? ConstantParameter的resolveInstance方法很簡單,就是返回parameters上對應(yīng)的值.
??? ComponentParameter稍顯復(fù)雜,和isResolvable類似,它需要在容器里面找到自己想要得值.先找到構(gòu)造函數(shù)參數(shù)類型的ComponentAdapter,
然后根據(jù)adapter返回實例.在他的resolveInstance方法中這樣寫到:return container.getComponentInstance
(componentAdapter.getComponentKey()),顯然如果有多層級聯(lián),會逐層實例下去.
??? 我們把找到的參數(shù)的值組成一個Object的數(shù)組.
? f.生成實例
??? 把構(gòu)造函數(shù)和參數(shù)發(fā)送給Object inst = newInstance(constructor, parameters);由newInstance返回一個實例.newInstance方法很簡單,調(diào)用jdk的方法:constructor.newInstance(parameters);
? g.大功告成.
?七.這里舉的是構(gòu)造注入的方式.設(shè)置注入在生成實例的部分和構(gòu)造注入有些區(qū)別,大體類似.
? 八.來看看與生命周期相關(guān)的內(nèi)容.
? 有生命周期的組件都能在pico注冊完成后通過調(diào)用start()方法用iterator模式逐個啟動--即調(diào)用每個組件的start()方法.
? 容器先啟動自己的所有有關(guān)的adapter, 然后去啟動所有子類的有關(guān)adapter.
? 啟動的命令是: this.lifecycleManager.start(this);
? 在DefaultPicoContainer里面,有個內(nèi)部類,實現(xiàn)了LifecycleManager接口.
? private LifecycleManager lifecycleManager = new OrderedComponentAdapterLifecycleManager();
? 我們來看OrderedComponentAdapterLifecycleManager的start()方法的實現(xiàn).
? 首先篩選有生命周期的adapter
?
for
?(
final
?Iterator?iter?
=
?adapters.iterator();?iter.hasNext();)?
{
????????????????
final
?ComponentAdapter?adapter?
=
?(ComponentAdapter)iter.next();

????????????????
if
?(?adapter?
instanceof
?LifecycleManager?)
{
????????????????????LifecycleManager?manager?
=
?(LifecycleManager)adapter;

????????????????????
if
?(manager.hasLifecycle())?
{
????????????????????????
//
?create?an?instance,?it?will?be?added?to?the?ordered?CA?list
????????????????????????adapter.getComponentInstance(node);
????????????????????????addOrderedComponentAdapter(adapter);
????????????????????}
????????????????}
????????????}
上面的意思是說.adapter必須實現(xiàn)LifecycleManager接口(通過UML圖可以看到,只有有限的幾個實現(xiàn)了他)而且實現(xiàn)類必須實現(xiàn)了startable.
參照CachingComponentAdapter構(gòu)造函數(shù)中下面的片斷
?this.delegateHasLifecylce = delegate instanceof LifecycleStrategy
??????????????? && ((LifecycleStrategy) delegate).hasLifecycle(delegate.getComponentImplementation());
然后對篩選出來的adapter逐個啟動
for
?(
final
?Iterator?iter?
=
?adapters.iterator();?iter.hasNext();)?
{
????????????????
final
?Object?adapter?
=
?iter.next();

????????????????
if
?(?adapter?
instanceof
?LifecycleManager?)
{
????????????????????LifecycleManager?manager?
=
?(LifecycleManager)adapter;
????????????????????manager.start(node);
????????????????????startedComponentAdapters.add(adapter);
????????????????}
????????????}
?? stop()和dispose()方法則直接利用start()篩選好的adapter.
----end----
??參考文檔:
????? http://dl.easyjf.com/downloads/stef_wu-PicoContainer-code.doc
?7/23/2006
?
?
?