輕量級IOC框架SwiftSuspenders


該框架的1.6版本位於https://github.com/tschneidereit/SwiftSuspenders/blob/the-past/,現在已經出了重新架構的2.0版本,所以我決定先研究已經成熟的1.6版本,有時間再研究2.0版本的。

 

IOC的基本知識

控制反轉(Inversion of Control,英文縮寫為IoC)又叫做依賴注入(Dependency Injection,英文縮寫為DI),是一種降低耦合度的程序設計模式,它通過將對象的創建過程解耦出來來降低對象間的依賴關系。具體的,在文章依賴注入那些事兒里解釋的很詳細,另外也可參考老馬的文章深度理解依賴注入(Dependence Injection)

 

describeType函數介紹

SwiftSuspenders是利用describeType函數來實現其反射機制的。

describeType函數可用來生成描述 ActionScript 對象(命名為方法的參數)的 XML 對象。利用此方法可實現 ActionScript 語言的反射編程概念。

為了詳細說明這個函數,我們先自定義一個類MyClass:

    public class MyClass extends Object
    {
        public const const_varable:int = 0;
        [Inject(desc="a public varable")]
        public var pub_varable:String = "";private var pri_varable:String = "";
        [Inject(desc="a static function")]
        public static function static_func(s:String):void
        {
            trace(s);
        }
        public function get accpri():String
        {
            return pri_varable;
        }
        public function set accpri(v:String):void
        {
            pri_varable = v;
        }
        public function TraceOut():void {
            trace(accpri);
        }
        public function MyClass()
        { 
        }
    }



如果傳入的參數是類型的實例,則返回的 XML 對象包括該類型的所有實例屬性,但不包括任何靜態屬性。
var m:MyClass = new MyClass();
trace(describeType(m));
得到如下的XML:

<type name="MyClass" base="Object" isDynamic="false" isFinal="false" isStatic="false">
  <extendsClass type="Object"/>
  <constant name="const_varable" type="int">
    <metadata name="__go_to_definition_help">
      <arg key="pos" value="110"/>
    </metadata>
  </constant>
  <variable name="pub_varable" type="String">
    <metadata name="Inject">
      <arg key="desc" value="a public varable"/>
    </metadata>
    <metadata name="__go_to_definition_help">
      <arg key="pos" value="184"/>
    </metadata>
  </variable>
  <accessor name="accpri" access="readwrite" type="String" declaredBy="MyClass">
    <metadata name="__go_to_definition_help">
      <arg key="pos" value="425"/>
    </metadata>
    <metadata name="__go_to_definition_help">
      <arg key="pos" value="498"/>
    </metadata>
  </accessor>
  <method name="TraceOut" declaredBy="MyClass" returnType="void">
    <metadata name="__go_to_definition_help">
      <arg key="pos" value="571"/>
    </metadata>
  </method>
  <metadata name="__go_to_ctor_definition_help">
    <arg key="pos" value="632"/>
  </metadata>
  <metadata name="__go_to_definition_help">
    <arg key="pos" value="67"/>
  </metadata>
</type>

 


忽略掉其中__go_to_ctor_definition_help形式的metadata,可以看到如下XML:

<type name="MyClass" base="Object" isDynamic="false" isFinal="false" isStatic="false">
  <extendsClass type="Object"/>
  <constant name="const_varable" type="int">
  </constant>
  <variable name="pub_varable" type="String">
    <metadata name="Inject">
      <arg key="desc" value="a public varable"/>
    </metadata>
  </variable>
  <accessor name="accpri" access="readwrite" type="String" declaredBy="MyClass">
  </accessor>
  <method name="TraceOut" declaredBy="MyClass" returnType="void">
  </method>
</type>

1.可以在解析 XML 對象時檢查傳入的是對象還是類,方法是檢查 <type> 標簽的 isStatic 屬性的值,該值在 value 參數是類型的實例時為 false。

2.private屬性和方法不會被describeType函數解析到。


要獲取某個類型的靜態屬性,則傳遞該類型本身。返回的 XML 對象不僅僅包括類型的靜態屬性,而且還包括它的所有實例屬性。
trace(describeType(MyClass));忽略掉其中__go_to_ctor_definition_help形式的metadata,可以得到如下XML:

<type name="MyClass" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <method name="static_func" declaredBy="MyClass" returnType="void">
    <parameter index="1" type="String" optional="false"/>
    <metadata name="Inject">
      <arg key="desc" value="a static function"/>
    </metadata>
  </method>
  <factory type="MyClass">
    <extendsClass type="Object"/>
    <constant name="const_varable" type="int">
    </constant>
    <variable name="pub_varable" type="String">
      <metadata name="Inject">
        <arg key="desc" value="a public varable"/>
      </metadata>
    </variable>
    <accessor name="accpri" access="readwrite" type="String" declaredBy="MyClass">
    </accessor>
    <method name="TraceOut" declaredBy="MyClass" returnType="void">
    </method>
  </factory>
</type>

 

1.若傳入參數為類,則實例屬性嵌套在名為 <factory> 的標簽內,從而與靜態屬性區分開來。在這種情況下,<type> 標簽的 isStatic 屬性為 true。

2.所有的非靜態方法將顯示在<factory> 標簽內,靜態方法顯示在<factory> 標簽外。

下表描述了由 describeType() 生成的 XML 對象的某些標簽和屬性(返回的所有類和接口名稱均采用完全限定的格式):

標簽 屬性 說明
<type>   XML 對象的根標簽。
  name ActionScript 對象的數據類型的名稱。
  base ActionScript 對象的定義類的直接超類。如果 ActionScript 對象是類對象,則值為 Class
  isDynamic 如果 ActionScript 對象的定義類是動態的,則為 true;否則為 false。如果 ActionScript 對象是類對象,則值為 true,因為 Class 類是動態的。
  isFinal 如果 ActionScript 對象的定義類是最終類,則為 true;否則為 false
  isStatic 如果 ActionScript 對象是類對象或構造函數,則為 true;否則為 false。此屬性之所以名為 isStatic,原因是:如果此屬性為 true,則未嵌套在 factory 標記內的任何標簽都是靜態的。
<extendsClass>   ActionScript 對象的定義類的每個超類都有一個單獨的 extendsClass 標簽。
  type ActionScript 對象的定義類擴展的超類的名稱。
<implementsInterface>   ActionScript 對象的定義類或其任何超類實現的每個接口都有一個單獨的 implementsInterface 標簽。
  type ActionScript 對象的定義類實現的接口的名稱。
<accessor>   存取器是 getter 和 setter 函數定義的一個屬性。
  name 存取器的名稱。
  access 屬性的訪問權限。可能的值包括 readonlywriteonlyreadwrite
  type 屬性的數據類型。
  declaredBy 包含關聯的 getter 或 setter 函數的類。
<constant>   常量是用 const 語句定義的一個屬性。
  name 常量的名稱。
  type 常量的數據類型。
<constructor>   在構造函數有參數時顯示。
<method>   方法是作為類定義的一部分聲明的函數。
  name 方法的名稱。
  declaredBy 包含方法定義的類。
  returnType 方法的返回值的數據類型。
<parameter>   方法定義的每個參數都有一個單獨的 parameter 標簽。此標簽始終嵌套在 <method> 標簽內。
  index 一個數字,對應於參數在方法的參數列表中出現的順序。第一個參數的值為 1。
  type 參數的數據類型。
  optional 如果參數是可選參數,則為 true;否則為 false
<variable>   變量是用 var 語句定義的一個屬性。
  name 變量的名稱。
  type 變量的數據類型。
<factory>   如果 ActionScript 對象是類對象或構造函數,則所有實例屬性和方法均嵌套在此標簽內。如果 <type> 標簽的 isStatic 屬性為 true,則未嵌套在 <factory> 標簽內的所有屬性和方法都是靜態的。只有在 ActionScript 對象是類對象或構造函數時,此標簽才會出現。
<metadata>   用[]括起來的標簽
  name 標簽的名稱。
<arg>   標簽中()括起來的參數
  key 參數的名稱。
  value 參數的值。

 

 在debug編譯條件下運行時我們能夠正確獲取metadata,但是release編譯條件下會忽略未知的metadata,這時,需要在flex sdk的編譯參數里面添加

-keep-as3-metadata+=MyMetadata

 

應用及API

1.定義依賴

Swiftsuspenders支持3種依賴定義:

a.值綁定,將一個注入請求返回為一個給定的對象

mapValue(whenAskedFor : Class, useValue : Object, named : String = "")

b.類綁定,將一個注入請求返回為一個給定類的新建的實例

mapClass(whenAskedFor : Class, instantiateClass : Class, named : String = "")

c.單例綁定,將所有的注入請求返回為同一個共享的實例,這個實例由第一次請求時創建。

mapSingleton(whenAskedFor : Class, named : String = "")

public function mapSingletonOf(whenAskedFor : Class, useSingletonOf : Class, named : String = "")

另外,Swiftsuspenders支持通過使用mapRule來擴展依賴映射

當一個類型在多處被注入,且需要使用不同的注入依賴時,通過指定注入名的方式來區別各處的注入。

 

2.定義注入點

Swiftsuspenders支持在4個位置進行注入:

a.屬性(如setter)

b.變量

c.方法(支持可選參數)

d.構造函數(支持可選參數)

 

3.變量注入的用例

    public interface IClass 
    {
        function ClassInfo():String;
    }

    public class ClassA implements IClass
    {
        
        public function ClassA() 
        {
            
        }
        public function ClassInfo():String
        {
            return "Class A";
        }
    }

    public class ClassB  implements IClass
    {
        
        public function ClassB() 
        {
            
        }
        public function ClassInfo():String
        {
            return "Class B";
        }
    }

    public class MyClass
    {
        [Inject]
        public var iclass:IClass;
        
        public function MyClass() 
        {
            
        }
        public function TraceOut():void
        {
            trace(iclass.ClassInfo());
        }
        
    }

        var inj :Injector = new Injector();

        inj.mapClass(MyClass, MyClass);
        inj.mapClass(IClass, ClassA);
        var a:MyClass = inj.getInstance(MyClass);
        inj.unmap(IClass);
        inj.mapClass(IClass, ClassB);
        var b:MyClass = inj.getInstance(MyClass);
        a.TraceOut();
        b.TraceOut();

 

4.構造函數注入的用例

 當對構造函數注入使用注入名時,元數據必須放在類定義的前面,而不是在構造函數前,這個是由於Flash Player的限制造成的。

只需修改上面代碼中的MyClass類:

    [Inject]
    public class MyClass
    {    
        public var iclass:IClass;
        
        
        public function MyClass(c:IClass) 
        {
            iclass = c;
        }
        public function TraceOut():void
        {
            trace(iclass.ClassInfo());
        }
        
    }

 

5.其他

a.可以使用PostConstruct來標注注入完成后自動調用的方法

有些需要在注入完成后調用的方法,可以申明[PostConstruct]元數據,從而在所有的注入完成后,自動調用被[PostConstruct]申明的方法。可以通過order參數來決定方法調用的順序,如:

[PostConstruct(order=1)]

 

b.可以用hasMapping來查詢某依賴是否已經存在,若是需要重新映射已經存在的依賴,需要先使用unmap方法解除原有依賴。

 

c.支持子注入器,通過createChildInjector方法實現。

 

d.支持通過XML文件配置注入信息。

 

設計結構

 

 

SwiftSuspenders的類圖如上圖所示。整個框架以Injector為核心。InjectionPoint包中包含了所有與注入點相關的類,InjectionResult包中包含了返回策略相關的類。

Injector中的m_mappings中包含所有的映射關系InjectionConfig,當調用mapClass,mapValue,mapSingleton時實際上就是建立類的映射關系,其序列圖如下:

 

 

m_injecteeDescriptions中包含InjecteeDescription對象,這個對象包括所有的注入點信息,包括構造注入點信息,和其他注入點信息。當getInstance時將根據之前配置的映射信息來獲取所有注入點,再通過根據注入點進行注入來獲取最終的對象,其流程圖如下:

這里面getInjectionPoints函數就是利用之前介紹的describeType函數來獲取類的內容,並進行解析以獲取注入點。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM