@Component在前篇Spring3 annotation搭配Struts1提到一顆飯粒,還提供@Autowired和@Inject來替代applicationContext.xml的Dependency Injection(DI,依賴注入),但這在Spring 2.5就有了。而@Configuration則才是Spring3提供的,我這兩天被這兩個annotation搞混,因為它們底下都有個@Bean的annoation。直到再做一顆飯粒,才煮熟了。

  經過@Component與@Configuration聲明的Class,把applicationContext.xml予以類別化,減少XML複雜化。不同在於@Component藉由在applicationContext.xml加注以下內容:

<context:annotation-config/>
<context:component-scan base-package="com.foo.demo"/>

  等同是applicationContext.xml委託給一個名叫<context:component-scan>的context代管。context:component-scan則搜尋bace-package底下有宣告@Component的類別(很意外,也包括@Configuration),取得宣告@Bean的method當作bean的設定,例如以下:

    @Bean       
    public IFooMgr iFooMgr() {
        logger.info("call IFooMgr");
        return new FooMgrImpl();
    }

  其作用等同於在applicationContext.xml如下的設定。其id採回傳的Class Name,並將第一個字母改為小寫。

<bean id="iFooMgr" class="com.foo.demo.mgr.impl.FooMgrImpl"/>

  若是像Struts1委託給Spring用例,因為bean id不能使用特殊符號如斜線,所以可以用@Bean(name="/login")的方式,等同於

<bean name="/login" class="com.foo.demo.action.LoginAction"/>

  而Spring bean在被配置時只能選id或name作為其唯一識別值,以參考以下code:

WebApplicationContext wctx =
    RequestContextUtils.getWebApplicationContext(request,
        request.getSession().getServletContext());
(1) IFooMgr foo1 = wctx.getBean(IFooMgr.class);       
(2) IFooMgr foo1 = (IFooMgr) wctx.getBean("iFooMgr");
(3) IFooMgr foo1 = (IFooMgr) wctx.getBean("foo1");

  (1)方式的取得不管@Bean有沒有設定name屬性都會work;(2)的方式若@Bean有設定name屬性會Exception;一樣,(3)的方式若未定設成@Bean(name="foo1")也照丟Exception。

  上面所述都是@Component的用法,為何能知context:component-scan會連宣告成@Configuration都會抓進context呢?藉由Log得知,WebApplicationContext在初始化時,都會呼叫有宣告@Bean的method一次。這種做法在有大量的bean時,啟動會很慢。而@Configuration所搭配的AnnotationConfigApplicationContext是可以lazy-init的,其料理作法如下:

// AnnotationConfigApplicationContext ctx
//    = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotationConfigApplicationContext ctx
    = new AnnotationConfigApplicationContext();
// ctx.register(MgrConfig.class, AppConfig.class, …);
ctx.scan("com.foo.demo");
ctx.refresh();

  宣告一個空的Context,也可以帶有宣告@Configuration的Class作為初始化參數;或是執行期才知道用哪個Config類別,再用register方式載入(可多個.class當參數往後加);亦可以比照<context:component-scan>方式用scan;想混用constructure、register及scan設定也應該可行(作法有點dirty)。最後千萬記住設定好要執行refresh,這樣getBean才有作用。
arrow
arrow
    全站熱搜

    Jemmy 發表在 痞客邦 留言(0) 人氣()