2010年6月27日 星期日

Spring 3.0 - IoC Container


初探一下Spring的整個核心精神 - IOC


1. org.springframework.beans


    BeanFactory 介面提供了更進階的設定機制用來管理各種object.


2. org.springframework.context


    ApplicationContext則是繼承了BeanFactory,並加入了更容易於整合Spring AOP的功能,像是


    message resource handling , event publication , application-layer context , 以及web applicaiton常需要的webApplicationContext.


    ApplicationContext的主要角色當然就是做為物件的初始化、參數設定,以及整合管理物件之間的使用關係,


    這一切都是直接透過 XML metadata / java annotation / java code 設定完成。


    常見用來做為設定的有ClassPathXmlApplicaitonContext / FileSystemXmlApplicationContext


    來個簡單的XML metadata


    <?xml version="1.0" encoding="UTF-8"?>

        <beans xmlns="http://www.springframework.org/schema/beans"

            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

            xsi:schemaLocation="http://www.springframework.org/schema/beans

            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

            <bean id="...">

            </bean>

            <!-- more bean definitions go here -->

        </beans>

    bean tag中標明的id,是用來作為與其他不同的bean instance共同工作(mabye as a arg in another bean)時的一個關連用的名稱。

    所以在這裏我們可以知道,原則上只要有在設定檔中宣告過的bean,其實都可以透過applicationcontext去取得實體,不需要自行去new一個出來用,參考以下範例:

    // create and configure beans

    ApplicationContext context =new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

    // retrieve configured instance

    PetStoreServiceImpl service = context.getBean("petStore", PetStoreServiceImpl.class);

    // use configured instance

    List userList =service.getUsernameList();


3. Bean instantiation ~


    這裡開始見到一些常見的Design pattern用法了,Spring在處理物件初始話時,選用了 static factory method,透過<bean> tag宣告,


    告知IOC container 去找到正確需要被呼叫的 factory method,整體的細節實作在更後面的章節會提到,先貼個sample來過過癮。


    <bean id="clientService"  factory-method="createInstance"/>


        public class ClientService {


            private static ClientService clientService = new ClientService();


            private ClientService() {}


            public static ClientService createInstance() {


                return clientService;


            }


        }


    除了上述的還有instance factory method


    每一個bean在宣告設定時,是可以有多個factory method的!!


    <bean id="serviceLocator">


        <!-- inject any dependencies required by this locator bean -->


    </bean>


    <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>


    <bean id="accountService"  factory-bean="serviceLocator"  factory-method="createAccountServiceInstance"/>


4. Dependencies


    DI,簡單講就是一個專門來處理個實體之間的相互關係的process,透過DI來處理各個物件實體之間是如何運作的,


    大致上有使用三種方式來進行 [ constructor / arg to factory method / setter  ],


    透過設定的方式來降低各個物件中的高度相依關係,同時也使得各項物件便於進行測試,每一個物件甚至根本不會知道彼此依存的物件的存在@@"


    在Spring 中建議的方式是:Constructor-based dependency injection & Setter-based dependency injection


    # Constructor-based DI


    透過建構子的方式進行注入的工作是把必要得參數傳入由container執行,當然這也等同於使用statis factory method來運作!! 以下範例說明僅提供Constructor DI


    public class A {


        private String name;


        public A(String name){ this.name = name;}


    }


    如果遇到constructor傳入參數眾多時,Container將自動進行ambigiously type checking, 不會去管參數傳入順序,只要找到傳入參數型別相符的,


    就會使用該種宣告的bean 定義 ( ex , 傳入 HashMap , LinkList 但與bean tag中宣告兩者相反順序), 當然也可以指定出現順序,如下範例:


    <bean id="exampleBean">

        <constructor-arg  index="0" value="7500000"/>

        <constructor-arg  index="1" value="42"/>

    </bean>


    # Setter-based DI


    透過Setter方式,是在bean實體化之後再進行的,當然一定是會搭配一個non-arg public constructor作宣告


    ApplicationContext支援Constructor-Based / Setter-Based DI


    那麼我們到底要用哪一種? Spring 建議使用Setter DI,因為過多的參數處理會讓你得寫一拖拉庫的factory method,


    尤其是遇到有很多參數是非必要的,那就麻煩了!! 但這只是建議,必要時是得看真實情形去設計。


    # Circular dependencies lead to BeanCurrentlyCreationException


    當定義class A instance  與 class B instance 彼此之間透過 constructor injection互相參考時,很容易發生這種問題,要避免這種問題就是選擇使用Setter DI(且也是唯一解)。


    Spring IOC  container會在load-time進行所有的設定檢查,包括那些不存在的bean 定義以及circular dependencies,


    但在設定相依性的部分則會較晚發生( lazy binding ?),這邊也就是說部會在一開始container load時就會將所有物件的錯誤全數引發,會在當你去要一個bean instance時,假設有exception才會丟出..


    #bean  定義特殊說明


    <bean id="" name="" factory-method="" > --> 使用factory method的只有在constructor DI方式下才有,Setter DI沒有


        <constructor-arg />--> Constructor DI使用


        <property /> --> Setter DI 使用


    </bean>


    參考到其他的bean時,可以使用以下方法


    <bean id="theTargetBean" />


    <bean id="client">


    <property name="targetName" value="theTargetBean" />


    </bean>


    但Spring建議使用以下方法


    <bean id="theTargetBean"/>


        <bean id="theClientBean">


            <property name="targetName">


                <idref bean="theTargetBean" />


            </property>


     </bean>


    原因是使用後者的方法時,IOC container可以在deploy time去做驗證確認bean instance是否已經存在,而前者則完全無法進行驗證


    # ref element in <constructor-arg> or <property>


    <ref bean=""> --> 可以在不同的xml 定義檔中reference


    <ref local=""> --> 僅能在同一個xml 定義檔中reference


    <ref parent=""> --> 這部分主要是可以透過不同的iocContainer來存取parent container 的bean


    # Inner beans ,這部分設定產出的其實就是巢狀的<bean> tag,只是他只能是anonymous class,且不需要id / name,bean scope 只會是prototype (每一個物件實體各自存在)


    # Lazy-initialized beans,預設的ApplicationContext是會自動的將大部份的singleton bean 進行初始化,若要避免此問題 可在<bean lazy-init="true">即可關閉,


    當然若要針對整個container層級進行設定,也可在<beans default-lazy-init="true">進行設定。


沒有留言: