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中所訂的一個beanpackage 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的理由...
沒有留言:
張貼留言