Spring在使用Annotation上有些舊包袱,但在JSR-299,JSR-330後也逐漸為大家接受,但是在3.0之前,仍需要一個XML設定檔,相較Guice完全不用的情形下是有些許的不便(當然2.5自己加工一下也是可以達成不用讀取XML而直接使用Annotaion)。
Spring3.0多了個AnnotationConfigApplicationContext,可以讓我們完全不用讀取任何XML的檔案就能依Annotation完成DI的組裝工作,下面就簡單列一下兩種DI Framework的做法吧。
package org.elliot.di; public interface Module { public String getModuleName(); }
package org.elliot.di; import org.springframework.stereotype.Component; @Component //Spring component => a bean public class DefaultModule implements Module{ public String getModuleName() { return "Default"; } }訂了一個非常沒用的Interface,再實作一個很無聊的Implementation,DefaultModule上訂的@Component是Spring自定的,也可以改用JSR-299所定的@Resource,這個的做用基本上就是將它當做是之前Spring xml configuration中所訂的一個bean
package org.elliot.di; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.google.inject.Inject; @Component //Spring component => a bean public class Service { @Autowired //Spring Autowired @Inject //Guice Inject private Module module; public Module getModule() { return module; } public void setModule(Module module) { this.module = module; } public void showModuleName() { System.out.println(this.module.getModuleName()); } }Service提供一個被注入的標的module,@Autowired是Spring的Annotaion,@Inject則是Guice的Annotaion,做用雷同,代表這是一個可以被注入的Field。
再來是Spring與Guice想法不同之處,Spring必需把Service也定為一個Component,這樣才可以透過BeanFactory或是Context取得,但Guice則不用,你可以留到你程式要用時再透過Guice Container來組裝,Spring目前似乎沒有這樣的想法(不確定...)。我比較想要的是可以自行new 一個Service instance,再丟給DI Container來將所需要的東西注入。
Guice雖然沒有設定檔,但你還是需要一個AbstractModule來指出一個組裝的需求,就像下列這樣,
package org.elliot.guice; import org.elliot.di.DefaultModule; import org.elliot.di.Module; import com.google.inject.AbstractModule; public class GuiceConfigModule extends AbstractModule { @Override protected void configure() { bind(Module.class).to(DefaultModule.class); } }必需要extends AbstractModule,實做protected void configure();這裡指定了只要Field型態是Module的都用DefaultModule的instance來注入。
再來就是簡單的測試,順便展示基本的用法
package org.elliot.guice; import static org.junit.Assert.assertNotNull; import org.elliot.di.Service; import org.junit.Before; import org.junit.Test; import com.google.inject.Guice; import com.google.inject.Injector; public class GuiceDITest { private Service service; @Before public void setUp() throws Exception { Injector injector = Guice.createInjector(new GuiceConfigModule()); service = injector.getInstance(Service.class); } @Test public void testGuice() { assertNotNull(service.getModule()); service.showModuleName(); } }這是Guice的簡單測試,例用Guice.createInjector來產生一個Injector,這個Injector就同於Spring的Context,你需要相關的instance都跟Injector要。
Spring的也很簡單
package org.elliot.spring; import static org.junit.Assert.assertNotNull; import org.elliot.di.Service; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class SpringDITest { private Service service; @Before public void setUp() throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("org.elliot"); service = context.getBean(Service.class); } @Test public void testGuice() { assertNotNull(service.getModule()); service.showModuleName(); } }基本上就是將之前常用的ClassPathXmlApplicationContext, FileSystemXmlApplicationContext換成AnnotationConfigApplicationContext。
兩個TestCase要做的事完全一樣,看得出來Spring也能縮減相當程度的複雜度,但是Guice在速度跟耗用記憶體上還是具有優勢,只是我又少了一個用Guice的理由...
沒有留言:
張貼留言