拾貝殼

          走過的路
          隨筆 - 39, 文章 - 1, 評論 - 14, 引用 - 0
          數(shù)據(jù)加載中……

          PicoContainer源碼導(dǎo)讀


          一、簡介
          ?? 感謝“簡易java框架”分享的學(xué)習(xí)心得。循著他的足跡,我把picocontainer讀了一遍。源代碼的版本是1.2-RC-2。
          ?? pico的官方站點:http://www.picocontainer.org/
          ?? 由于它是一個專門的ioc容器,所以使用起來沒有spring那么麻煩。關(guān)于他的文檔,在官方站點上有一篇《5分鐘搞定pico》的文章。國人似乎也有很多的翻譯版本。講解得很詳細(xì),大家可以看看。
          二、快速入手
          ?? 先來體驗一下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,看看運行的流程。
          ?我們先來測試默認(rèn)的用法:

          ????? 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一下上面提到的測試?yán)?
          ?1.調(diào)用DefaultPicoContainer(ComponentAdapterFactory componentAdapterFactory,LifecycleStrategy

          lifecycleStrategyForInstanceRegistrations,PicoContainer parent)
          ?? parent可以為null,其他2個如果沒有設(shè)置,將用默認(rèn)的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)細(xì)節(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)表達(dá)式前半句對基本類型作了處理.后半句判斷注冊的參數(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;
          ????}


          ? 顯然,他默認(rèn)設(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
          ?
          ?
          ?

          posted on 2006-07-23 14:30 binge 閱讀(3025) 評論(0)  編輯  收藏 所屬分類: J2EEOPEN SOURCE

          主站蜘蛛池模板: 鹤峰县| 西华县| 弥勒县| 云霄县| 凤翔县| 舒兰市| 鹿邑县| 平凉市| 卢湾区| 江孜县| 闽侯县| 吉首市| 白河县| 定襄县| 井冈山市| 乌海市| 高青县| 广汉市| 沁源县| 泗洪县| 灯塔市| 苍梧县| 柳江县| 平和县| 景宁| 留坝县| 新邵县| 古丈县| 苗栗县| 浦北县| 长海县| 临泽县| 长子县| 乃东县| 石景山区| 剑河县| 英山县| 天祝| 辽阳县| 平陆县| 芜湖市|