今天HiveMind 1.0 的final版本出來了, 看了看他的examples代碼。 有了IoC(DI)的感覺之後再看這些代碼, 越看感覺越清晰。 實現一個IoC的容器本身沒什麼可說的, 現在就以他自帶的example爲例,來看看HiveMind在這方面的實現方法。
example程序是一個四則運算的類,基本思想是將加減乘除都做成接口,用不同的方式實現;計算器(Caculator)繼承了加減乘除接口,在運行過程中,具體的加減乘除操作類通過HiveMind的配置注入到CaculatorImpl中,當然CaculatorImpl也是通過Caculator接口通過工廠產生出來的,以下是他的具體代碼:
double arg0 = Double.parseDouble(args[0]);
double arg1 = Double.parseDouble(args[1]);
Registry registry = ExampleUtils.buildRegistry("examples.xml");
// Since we know there's exactly *one* service-point implementing Calculator,
// we can get it this way, and never have to know its service id.
Calculator calculator = (Calculator) registry.getService(Calculator.class);
System.out.println("Inputs: " + arg0 + " and " + arg1);
System.out.println("Add: " + calculator.add(arg0, arg1));
System.out.println("Subtract: " + calculator.subtract(arg0, arg1));
System.out.println("Multiply: " + calculator.multiply(arg0, arg1));
System.out.println("Divide: " + calculator.divide(arg0, arg1));
registry.shutdown();
嗯,Registry registry = ExampleUtils.buildRegistry("examples.xml"); 這條語句看來是從examples.xml中進行相應的初始化並建立對應關係了,內部大概是根據配置文件定義攔截器,工廠,初始化方法之類,不用看。這裏的Registry應該等於Spring中的ApplicationContext, Pico中的Configuration了。看了IoC容器這方面的也沒什麼別的東西,一定得有一個全局的東西hold這些被管理的類的。
下面的registry.getService(Calculator.class);看起來要比Spring的appContext.getBean(beanId)方便一點,在整個配置文件保證藉口唯一的前提下,可以直接採用class作爲參數取出對象。當然Spring完全可以這麼做,只看Johnson先生高興不高興了。
Caculator接口繼承了增刪改查接口(就是4個各包含一個方法的接口)。
再看看examples.xml配置:
<module id="examples" version="1.0.0">
<service-point id="Adder" interface="org.apache.hivemind.examples.Adder">
<create-instance class="org.apache.hivemind.examples.impl.AdderImpl"/>
<interceptor service-id="hivemind.LoggingInterceptor"/>
</service-point>
...其他操作省略
<service-point id="Calculator" interface="org.apache.hivemind.examples.Calculator">
<invoke-factory>
<!-- Most properties are autowired by the BuilderFactory -->
<construct class="org.apache.hivemind.examples.impl.CalculatorImpl"/>
</invoke-factory>
<interceptor service-id="hivemind.LoggingInterceptor"/>
</service-point>
</module>
仔細看看這個配置文件就可以看出一些有趣的東西:
service-point毫無疑問等於Spring中的bean。(Howard同志一向以長的配置名稱聞名,這一點可以在Tapestry的配置文件中得到證實,不過比起Spring的超長package name和constant name,似乎又差了一點,呵呵),id, interface……等等,看來hlship完全不鼓勵在這個Container中使用具體的類了,看看DTD文件的定義:
<service-point id=".." interface=".."
[parameters-schema-id=".."]
[parameters-occurs="unbounded |
0..1 | 1 | 1..n | none"]>
[parameters-schema]
[create-instance]
[invoke-factory]
[interceptor]
</service-point>
確實沒有class這個屬性……這樣做好還是不好?……不知道,完全面向接口的系統存在嗎……這個問題暫時不想,以後再說。四個加減乘除的類的生成沒什麼好說的,看看
<service-point id="Adder" interface="org.apache.hivemind.examples.Adder">
<create-instance class="org.apache.hivemind.examples.impl.AdderImpl"/>
<interceptor service-id="hivemind.LoggingInterceptor"/>
</service-point>
的意思,應該是創建一個以org.apache.hivemind.examples.impl.AdderImpl的實例,從<create-instance>的DTD看來,他允許創建爲primitive, singleton, threaded, pooled的形式。默認應該是每次調用創建一個實例吧,我猜。然後用一個攔截器(LogginInterceptor)來處理。
這裏又發現了一個比Spring要方便的地方,interceptor可以直接定義在(我都不知道怎麼說了,用Bean還是service-point?)Component的內部,用Spring的話還得另外建立一個新的Bean,然後指定Advice的作用域,如果系統中只有一兩處需要的話,多一個Bean的配置顯得有點不雅。記得xWork也是這樣定義interceptor的。
下面的按照工廠形式創建實例有點意思。
<service-point id="Calculator" interface="org.apache.hivemind.examples.Calculator">
<invoke-factory>
<!-- Most properties are autowired by the BuilderFactory -->
<construct class="org.apache.hivemind.examples.impl.CalculatorImpl"/>
</invoke-factory>
<interceptor service-id="hivemind.LoggingInterceptor"/>
</service-point>
先看看CaculatorImpl的實現:
public class CalculatorImpl implements Calculator {
private Adder _adder;
private Subtracter _subtracter;
private Multiplier _multiplier;
private Divider _divider;
public double add(double arg0, double arg1) {
return _adder.add(arg0, arg1);
}
...後面的減乘除就不寫了,類似
...一堆的setter/getter就不寫了
}
剛開始詫異了一下,在我感覺裏,這裏怎麼說應該有個輸入參數的地方,就象下面:
<invoke-factory>
<construct class="org.apache.hivemind.examples.impl.CalculatorImpl">
<set-property name="adder" ref="Adder" />
...
</construct>
</invoke-factory>
看看他的註釋:Most properties are autowired by the BuilderFactory,看樣子他在BuilderFactory中默認將同id的service-point注入到construct中去了,這種便利是否必要?畢竟遍歷一個類的set方法,判斷方法所需的類型,尋找registry中的service-point然後注入,這都是需要代價的……沒想清楚,暫時放下。
題外話:我沒有一直跟隨HiveMind的版本變化,但在我的記憶中,1.0的某個rc版本將配置文件換成了Howard同志自己發明的Simple Data Language,其實就是hlship根據JavaCC自己組織了一套語法,然後將所有的配置文件用這種語法改寫……沒多少日子這個東西就被pass掉了。直到現在我還懷疑他做這件事情的動機,目前最能讓我覺得有趣的一種解釋是:Howard看到JavaCC很強大,能夠很輕易的定義一種新的語法並解析,具備Tapestry全新的創意的Howard,一時頭腦發熱就加入了這個東東……呵呵
HiveMind還有一些其他的特性,比如系統所有配置的文檔生成(這個功能Spring加上就好了)以及其他的一些方便的特性。Howard一再強調HiveMind是一個Micro Kernel的框架,但在我看來,他是一個新的,可能更加方便的,完全面向接口的,基於IoC的容器。