01 - 依賴注入
1. 依賴注入
1.1 類依賴注入
所謂的綁定就是將一個接口綁定到具體的類中,這樣客戶端不用關心具體的實現,而只須要獲取相應的接口完成其服務便可。
HelloWorld.java
1 public interface HelloWorld {
2
3 String sayHello();
4 }
5
而後是具體的實現,HelloWorldImpl.java
1 public class HelloWorldImpl implements HelloWorld {
2
3 @Override
4 public String sayHello() {
5 return "Hello, world!";
6 }
7 }
8
寫一個測試例子看看,HelleWorldTest.java
1 public class HelleWorldTest {
2
3 @Test
4 public void testSayHello() {
5 Injector inj= Guice.createInjector(new Module() {
6 @Override
7 public void configure(Binder binder) {
8 binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
9 }
10 });
11 HelloWorld hw = inj.getInstance(HelloWorld.class);
12 Assert.assertEquals(hw.sayHello(), "Hello, world!");
13 }
14 }
15
這個例子很是簡單,通俗的將就是將一個HelloWorldImpl的實例與HelloWorld關聯起來,當想Guice獲取一個 HelloWorld實例的時候,Guice就返回一個HelloWorldImpl的實例,而後咱們就能夠調用HelloWorld服務的方法了。
問題(1)HelloWorld是單例的麼?測試下。
1 HelloWorld hw = inj.getInstance(HelloWorld.class);
2 Assert.assertEquals(hw.sayHello(), "Hello, world!");
3 HelloWorld hw2 = inj.getInstance(HelloWorld.class);
4 System.out.println(hw.hashCode()+"->"+hw2.hashCode());
5 Assert.assertEquals(hw.hashCode(), hw2.hashCode());
解答(1)測試結果告訴咱們,HelloWorld不是單例的,每次都會返回一個新的實例。
問題(2)HelloWorld的實例是HelloWorldImpl麼?能夠強制轉型麼?
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.getClass().getName());
解答(2),結果輸出cn.imxylz.study.guice.helloworld.HelloWorldImpl,看來確實只是返回了一個正常的實例,並無作過多的轉換和代理。
問題(3),若是綁定多個實現到同一個接口上會出現什麼狀況?
1 public class HelloWorldImplAgain implements HelloWorld {
2 @Override
3 public String sayHello() {
4 return "Hello world again.";
5 }
6 }
binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class);
解答(3),很不幸,Guice目前看起來不容許多個實例綁定到同一個接口上了。
com.google.inject.CreationException: Guice creation errors:
1) A binding to cn.imxylz.study.guice.helloworld.HelloWorld was already configured at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28).
at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:29)
問題(4),能夠綁定一個實現類到實現類麼?
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorldImpl.class).to(HelloWorldImpl.class);
5 }
6 });
7 HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
8 System.out.println(hw.sayHello());
很是不幸,不能夠本身綁定到本身。
1) Binding points to itself.
at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28)
咱們來看看bind的語法。
<T> AnnotatedBindingBuilder<T> bind(Class<T> type);
ScopedBindingBuilder to(Class<? extends T> implementation);
也就是說只能綁定一個類的子類到其自己。改造下,改用子類替代。
1 public class HelloWorldSubImpl extends HelloWorldImpl {
2
3 @Override
4 public String sayHello() {
5 return "@HelloWorldSubImpl";
6 }
7 }
8
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorldImpl.class).to(HelloWorldSubImpl.class);
5 }
6 });
7 HelloWorldImpl hw = inj.getInstance(HelloWorldImpl.class);
8 System.out.println(hw.sayHello());
太好了,支持子類綁定,這樣即便咱們將一個實現類發佈出去了(儘管不推薦這麼作),咱們在後期仍然有辦法替換實現類。
使用bind有一個好處,因爲JAVA 5以上的泛型在編譯器就肯定了,因此能夠幫咱們檢測出綁定錯誤的問題,而這個在配置文件中是沒法檢測出來的。
這樣看起來Module像是一個Map,根據一個Key獲取其Value,很是簡單的邏輯。
問題(5),能夠綁定到咱們本身構造出來的實例麼?
解答(5)固然能夠!看下面的例子。
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorld.class).toInstance(new HelloWorldImpl());
5 }
6 });
7 HelloWorld hw = inj.getInstance(HelloWorld.class);
8 System.out.println(hw.sayHello());
問題(6),我不想本身提供邏輯來構造一個對象能夠麼?
解答(6),能夠Guice提供了一個方式(Provider<T>),容許本身提供構造對象的方式。
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorld.class).toProvider(new Provider<HelloWorld>() {
5 @Override
6 public HelloWorld get() {
7 return new HelloWorldImpl();
8 }
9 });
10 }
11 });
12 HelloWorld hw = inj.getInstance(HelloWorld.class);
13 System.out.println(hw.sayHello());
問題(7),實現類能夠不通過綁定就獲取麼?好比我想獲取HelloWorldImpl的實例而不經過Module綁定麼?
解答(7),能夠,實際上Guice可以自動尋找實現類。
Injector inj= Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
System.out.println(hw.sayHello());
問題(8),可使用註解方式完成注入麼?不想手動關聯實現類。
解答(8),好,Guice提供了註解的方式完成關聯。咱們須要在接口上指明此接口被哪一個實現類關聯了。
1 @ImplementedBy(HelloWorldImpl.class)
2 public interface HelloWorld {
3
4 String sayHello();
5 }
6
Injector inj= Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.sayHello());
事實上對於一個已經被註解的接口咱們仍然可使用Module來關聯,這樣獲取的實例將是Module關聯的實例,而不是@ImplementedBy註解關聯的實例。這樣仍然遵循一個原則,手動優於自動。
問題(9)再回頭看問題(1)怎麼綁定一個單例?
1 Injector inj = Guice.createInjector(new Module() {
2
3 @Override
4 public void configure(Binder binder) {
5 binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class).in(Scopes.SINGLETON);
6 }
7 });
8 HelloWorld hw = inj.getInstance(HelloWorld.class);
9 HelloWorld hw2 = inj.getInstance(HelloWorld.class);
10 System.out.println(hw.hashCode() + "->" + hw2.hashCode());
11
能夠看到如今獲取的實例已是單例的,再也不每次請求生成一個新的實例。事實上Guice提供兩種 Scope,com.google.inject.Scopes.SINGLETON和 com.google.inject.Scopes.NO_SCOPE,所謂沒有scope便是每次生成一個新的實例。
對於自動注入就很是簡單了,只須要在實現類加一個Singleton註解便可。
1 @Singleton
2 public class HelloWorldImpl implements HelloWorld {
3
4 @Override
5 public String sayHello() {
6 return "Hello, world!";
7 }
8 }
9java
Google Guice 入門教程02 - 依賴注入
1.2 屬性注入(Field Inject)
1.2.1 基本屬性注入
首先來看一個例子。Service.java
1 @ImplementedBy(ServiceImpl.class)
2 public interface Service {
3 void execute();
4 }
ServiceImpl.java
1 public class ServiceImpl implements Service {
2 @Override
3 public void execute() {
4 System.out.println("This is made by imxylz (www.imxylz.cn).");
5 }
6 }
FieldInjectDemo.java
1 /** a demo with Field inject
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 71 $
4 */
5 public class FieldInjectDemo {
6 @Inject
7 private Service servcie;
8 public Service getServcie() {
9 return servcie;
10 }
11 public static void main(String[] args) {
12 FieldInjectDemo demo = Guice.createInjector().getInstance(FieldInjectDemo.class);
13 demo.getServcie().execute();
14 }
15 }
這個例子比較簡單。具體來講就是將接口Service經過@Inject註解注入到FieldInjectDemo類中,而後再 FieldInjectDemo類中使用此服務而已。固然Service服務已經經過@ImplementedBy註解關聯到ServiceImpl 類中,每次生成一個新的實例(非單例)。注意,這裏FieldInjectDemo類沒有經過Module等關聯到Guice中,具體能夠查看《》。
意料之中獲得了咱們期待的結果。
一樣,咱們經過問答的方式來加深理解(注意,入門教程咱們只是強調怎麼使用,至於原理和底層的思想咱們放到高級教程中再談)。
問題(1):能夠本身構造FieldInjectDemo 對象而不經過Guice麼?
1 /** field inject demo2
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 73 $
4 */
5 public class FieldInjectDemo2 {
6 @Inject
7 private Service servcie;
8 public Service getServcie() {
9 return servcie;
10 }
11 public static void main(String[] args) {
12 FieldInjectDemo2 fd = new FieldInjectDemo2();
13 fd.getServcie().execute();
14 }
15 }
就像上面的例子中同樣,而後運行下看看?很是不幸,咱們獲得了一個誰都不喜歡的結果。
Exception in thread "main" java.lang.NullPointerException
at cn.imxylz.study.guice.inject.FieldInjectDemo2.main(FieldInjectDemo2.java:22)
很顯然,因爲FieldInjectDemo2不屬於Guice容器(暫且稱爲容器吧)託管,這樣Service服務沒有機會被注入到FieldInjectDemo2類中。
問題(2):能夠注入靜態屬性麼?
看下面的代碼。
1 public class FieldInjectDemo2 {
2 @Inject
3 private static Service servcie;
4 public static Service getServcie() {
5 return servcie;
6 }
7 public static void main(String[] args) {
8 FieldInjectDemo2 fd = Guice.createInjector().getInstance(FieldInjectDemo2.class);
9 FieldInjectDemo2.getServcie().execute();
10 }
11 }
很不幸!運行結果告訴咱們Guice看起來還不支持靜態字段注入。
好了,上面兩個問題咱們暫且放下,咱們繼續學習其它注入功能。
1.2.2 構造函數注入(Constructor Inject)
繼續看例子。例子是說明問題的很好方式。
1 /**
2 * $Id: ConstructorInjectDemo.java 75 2009-12-23 14:22:35Z xylz $
3 * xylz study project (www.imxylz.cn)
4 */
5 package cn.imxylz.study.guice.inject;
6
7 import com.google.inject.Guice;
8 import com.google.inject.Inject;
9
10 /** a demo with constructor inject
11 * @author xylz (www.imxylz.cn)
12 * @version $Rev: 75 $
13 */
14 public class ConstructorInjectDemo {
15
16 private Service service;
17 @Inject
18 public ConstructorInjectDemo(Service service) {
19 this.service=service;
20 }
21 public Service getService() {
22 return service;
23 }
24 public static void main(String[] args) {
25 ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
26 cid.getService().execute();
27 }
28
29 }
30
31
咱們在構造函數上添加@Inject來達到自動注入的目的。構造函數注入的好處是能夠保證只有一個地方來完成屬性注入,這樣能夠確保在構造函數中完成一些初始化工做(儘管不推薦這麼作)。固然構造函數注入的缺點是類的實例化與參數綁定了,限制了實例化類的方式。
問題(3):構造函數中能夠自動注入多個參數麼?
1 public class ConstructorInjectDemo {
2
3 private Service service;
4 private HelloWorld helloWorld;
5 @Inject
6 public ConstructorInjectDemo(Service service,HelloWorld helloWorld) {
7 this.service=service;
8 this.helloWorld=helloWorld;
9 }
10 public Service getService() {
11 return service;
12 }
13 public HelloWorld getHelloWorld() {
14 return helloWorld;
15 }
16 public static void main(String[] args) {
17 ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
18 cid.getService().execute();
19 System.out.println(cid.getHelloWorld().sayHello());
20 }
21 }
22
23
很是完美的支持了多參數構造函數注入。固然了沒有必要寫多個@Inject,並且寫了的話不能經過編譯。
1.2.3 Setter注入(Setter Method Inject)
有了上面的基礎咱們再來看Setter注入就很是簡單了,只不過在setter方法上增長一個@Inject註解而已。
1 public class SetterInjectDemo {
2
3 private Service service;
4
5 @Inject
6 public void setService(Service service) {
7 this.service = service;
8 }
9
10 public Service getService() {
11 return service;
12 }
13
14 public static void main(String[] args) {
15 SetterInjectDemo sid = Guice.createInjector().getInstance(SetterInjectDemo.class);
16 sid.getService().execute();
17 }
18
19 }
20
21
好了咱們再回頭看問題2的靜態注入(static inject)。下面的例子演示瞭如何注入一個靜態的字段。
1 /** a demo for static field inject
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 78 $
4 */
5 public class StaticFieldInjectDemo {
6
7 @Inject
8 private static Service service;
9
10 public static void main(String[] args) {
11 Guice.createInjector(new Module() {
12 @Override
13 public void configure(Binder binder) {
14 binder.requestStaticInjection(StaticFieldInjectDemo.class);
15 }
16 });
17 StaticFieldInjectDemo.service.execute();
18 }
19 }
20
21
很是棒!上面咱們並無使用Guice獲取一個StaticFieldInjectDemo實例(廢話),實際上static字段(屬性)是類相關的,因 此咱們須要請求靜態注入服務。可是一個好處是在外面看起來咱們的服務沒有Guice綁定,甚至client不知道(或者不關心)服務的注入過程。
再回到問題(1),參考上面靜態注入的過程,咱們可使用下面的方式來注入實例變量的屬性。
1 public class InstanceFieldInjectDemo {
2
3 @Inject
4 private Service service;
5 public static void main(String[] args) {
6 final InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
7 Guice.createInjector(new Module() {
8 @Override
9 public void configure(Binder binder) {
10 binder.requestInjection(ifid);
11 }
12 });
13 ifid.service.execute();
14 }
15 }
16
17
實際上這裏有一種簡便的方法來注入字段,實際上此方法也支持Setter注入。
1 public class InstanceFieldInjectDemo {
2
3 @Inject
4 private Service service;
5 public static void main(String[] args) {
6 InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
7 Guice.createInjector().injectMembers(ifid);
8 ifid.service.execute();
9 }
10 }
11
12
好了既然是入門教程,咱們就不討論更深層次的東西了。web
Google Guice 入門教程03 - 依賴注入
1.3 更多話題
1.3.1 接口多實現
若是一個接口有多個實現,這樣經過@Inject和Module都難以直接實現,可是這種現象確實是存在的,因而Guice提供了其它注入方式來解決此問題。好比下面的自定義註解。
1 public interface Service {
2
3 void execute();
4 }
5
6
1 public class HomeService implements Service {
2 @Override
3 public void execute() {
4 System.out.println("home.imxylz.cn");
5 }
6 }
1 public class WwwService implements Service {
2 @Override
3 public void execute() {
4 System.out.println("www.imxylz.cn");
5 }
6 }
1 @Retention(RetentionPolicy.RUNTIME)
2 @Target({FIELD,PARAMETER})
3 @BindingAnnotation
4 public @interface Home {
5 }
1 @Retention(RetentionPolicy.RUNTIME)
2 @Target({FIELD,PARAMETER})
3 @BindingAnnotation
4 public @interface Www {
5 }
上面的代碼描述的是一個Service服務,有WwwService和HomeService兩個實現,同時有Www和Home兩個註解(若是對註解各個參數不明白的須要單獨去學習JAVA 5註解)。好了下面請出咱們的主角。
1 /**
2 * $Id: MultiInterfaceServiceDemo.java 82 2009-12-24 06:55:16Z xylz $
3 * xylz study project (www.imxylz.cn)
4 */
5 package cn.imxylz.study.guice.inject.more;
6
7 import com.google.inject.Binder;
8 import com.google.inject.Guice;
9 import com.google.inject.Inject;
10 import com.google.inject.Module;
11
12 /** a demo with multi interfaces
13 * @author xylz (www.imxylz.cn)
14 * @version $Rev: 82 $
15 */
16 public class MultiInterfaceServiceDemo {
17 @Inject
18 @Www
19 private Service wwwService;
20 @Inject
21 @Home
22 private Service homeService;
23 public static void main(String[] args) {
24 MultiInterfaceServiceDemo misd = Guice.createInjector(new Module() {
25 @Override
26 public void configure(Binder binder) {
27 binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class);
28 binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class);
29 }
30 }).getInstance(MultiInterfaceServiceDemo.class);
31 misd.homeService.execute();
32 misd.wwwService.execute();
33 }
34 }
35
36
此類的結構是注入兩個Service服務,其中wwwService是注入@Www註解關聯的WwwService服務,而homeService是注入@Home註解關聯的HomeService服務。
一樣關於此結構咱們要問幾個問題。
問題(1)靜態注入多個服務怎麼寫?
其實,參照教程02,咱們可使用下面的例子。
1 public class StaticMultiInterfaceServiceDemo {
2 @Inject
3 @Www
4 private static Service wwwService;
5 @Inject
6 @Home
7 private static Service homeService;
8 public static void main(String[] args) {
9 Guice.createInjector(new Module() {
10 @Override
11 public void configure(Binder binder) {
12 binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class);
13 binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class);
14 binder.requestStaticInjection(StaticMultiInterfaceServiceDemo.class);
15 }
16 });
17 StaticMultiInterfaceServiceDemo.homeService.execute();
18 StaticMultiInterfaceServiceDemo.wwwService.execute();
19 }
20 }
問題(2):若是不當心一個屬性綁定了多個接口怎麼辦?
很是不幸,你將獲得相似一下的錯誤,也就是說不能夠綁定多個服務。
1) cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService has more than one annotation annotated with @BindingAnnotation: cn.imxylz.study.guice.inject.more.Www and cn.imxylz.study.guice.inject.more.Home
at cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService(StaticMultiInterfaceServiceDemo.java:17)
問題(3):我太懶了不想寫註解來區分多個服務,怎麼辦?
程序員都是懶惰的,因而Google幫咱們提供了一個Names的模板來生成註解。看下面的例子。
1 public class NoAnnotationMultiInterfaceServiceDemo {
2 @Inject
3 @Named("Www")
4 private static Service wwwService;
5 @Inject
6 @Named("Home")
7 private static Service homeService;
8 public static void main(String[] args) {
9 Guice.createInjector(new Module() {
10 @Override
11 public void configure(Binder binder) {
12 binder.bind(Service.class).annotatedWith(Names.named("Www")).to(WwwService.class);
13 binder.bind(Service.class).annotatedWith(Names.named("Home")).to(HomeService.class);
14 binder.requestStaticInjection(NoAnnotationMultiInterfaceServiceDemo.class);
15 }
16 });
17 NoAnnotationMultiInterfaceServiceDemo.homeService.execute();
18 NoAnnotationMultiInterfaceServiceDemo.wwwService.execute();
19 }
20 }
上面的例子中咱們使用Named來標註咱們的服務應該使用什麼樣的註解,固然前提是咱們已經將相應的服務與註解關聯起來了。
1.3.2 Provider注入
在教程第一篇中咱們提到了能夠經過Provider注入一個服務,這裏詳細說說這種模式。
首先咱們須要構造一個Provider<T>出來。
1 public class WwwServiceProvider implements Provider<Service> {
2
3 @Override
4 public Service get() {
5 return new WwwService();
6 }
7 }
8
9
上面的Provider的意思很簡單,每次新建一個新的WwwService對象出來。
注入的過程看下面的代碼。
1 public class ProviderServiceDemo {
2
3 @Inject
4 private Service service;
5
6 public static void main(String[] args) {
7 Injector inj= Guice.createInjector(new Module() {
8 @Override
9 public void configure(Binder binder) {
10 binder.bind(Service.class).toProvider(WwwServiceProvider.class);
11 }
12 });
13 ProviderServiceDemo psd = inj.getInstance(ProviderServiceDemo.class);
14 psd.service.execute();
15 }
16
17 }
18
19
很顯然若是這東西和線程綁定就很是好了,好比咱們可使用ThreadLocal來作線程的對象交換。
固然若是想自動注入(不使用Module手動關聯)服務的話,可使用@ProviderBy註解。
1 @ProvidedBy(WwwServiceProvider.class)
2 public interface Service {
3
4 void execute();
5 }
6
7
這樣咱們就沒必要使用Module將Provider綁定到Service上,獲取服務就很簡單了。
ProviderServiceDemo psd = Guice.createInjector().getInstance(ProviderServiceDemo.class);
psd.service.execute();
除了上述兩種方式咱們還能夠注入Provider,而不是注入服務,好比下面的例子例子中,屬性再也不是Service,而是一個Provider<Service>。
1 public class ProviderServiceDemo {
2
3 @Inject
4 private Provider<Service> provider;
5
6 public static void main(String[] args) {
7 ProviderServiceDemo psd = Guice.createInjector(new Module() {
8 @Override
9 public void configure(Binder binder) {
10 binder.bind(Service.class).toProvider(WwwServiceProvider.class);
11 }
12 }).getInstance(ProviderServiceDemo.class);
13 psd.provider.get().execute();
14 }
15 }
16
17
固然了,因爲咱們WwwServiceProvider每次都是構造一個新的服務出來,所以在類ProviderServiceDemo中的provider每次獲取的服務也是不同的。
1.3.3 綁定常量
看看下面的例子,演示了一個綁定整數值到實例的例子。
1 public class ConstantInjectDemo {
2
3 @Inject
4 @Named("v")
5 private int v;
6 public static void main(String[] args) {
7
8 ConstantInjectDemo cid = Guice.createInjector(new Module() {
9 @Override
10 public void configure(Binder binder) {
11 binder.bindConstant().annotatedWith(Names.named("v")).to(12);
12 }
13 }).getInstance(ConstantInjectDemo.class);
14 System.out.println(cid.v);
15 }
16 }
17
18
固然,既然可使用Named,也就可使用本身寫註解了。可是看起來好像沒有多大做用。除了上述寫法,也能夠用下面的方式實現。
binder.bind(int.class).annotatedWith(Names.named("v")).toInstance(12);
除了能夠綁定int外,在ConstantBindingBuilder類中還能夠綁定其它的基本類型。
com.google.inject.binder.ConstantBindingBuilder.to(String)
com.google.inject.binder.ConstantBindingBuilder.to(long)
com.google.inject.binder.ConstantBindingBuilder.to(boolean)
com.google.inject.binder.ConstantBindingBuilder.to(double)
com.google.inject.binder.ConstantBindingBuilder.to(float)
com.google.inject.binder.ConstantBindingBuilder.to(short)
com.google.inject.binder.ConstantBindingBuilder.to(char)
1.3.4 綁定Properties
除了能夠綁定基本類型外,還能夠綁定一個Properties到Guice中,固然了,因爲Properties本質上時一個Map<String,String>,所以Guice也容許綁定一個Map<String,String>。
1 @Inject
2 @Named("web")
3 private String web;
4
5 public static void main(String[] args) {
6
7 ConstantInjectDemo cid = Guice.createInjector(new Module() {
8 @Override
9 public void configure(Binder binder) {
10 Properties properties= new Properties();
11 properties.setProperty("web", "www.imxylz.cn");
12 Names.bindProperties(binder, properties);
13 }
14 }).getInstance(ConstantInjectDemo.class);
15 System.out.println(cid.web);
16 }
17
18ide
Google Guice 入門教程04 - 依賴注入
本章節繼續討論依賴注入的其餘話題,包括做用域(scope,這裏有一個與線程綁定的做用域例子)、當即初始化(Eagerly Loading Bindings)、運行階段(Stage)、選項注入(Optional Injection)等等。
1.3.5 Scope(做用域)
在1.1章節中咱們初步瞭解了對象的單例模式,在Guice中提供了一些常見的做用域,好比對於單例模式有下面兩個做用域。
com.google.inject.Scopes.SINGLETON
com.google.inject.Scopes.NO_SCOPE
在使用上,可使用Module的bind來實現,看下面的例子。
1 public class ScopeDemo {
2 public static void main(String[] args) {
3
4 Service service = Guice.createInjector(new Module() {
5 @Override
6 public void configure(Binder binder) {
7 binder.bind(Service.class).to(WwwService.class).in(Scopes.SINGLETON);
8 }
9 }).getInstance(Service.class);
10 service.execute();
11 }
12 }
13
14
固然單例模式還能夠彷佛用@Singleton註解。
在com.google.inject.binder.ScopedBindingBuilder.in(Scope)方法中,一個Scope除了可使 上面的SINGLETION和NO_SCOPE外,還能夠是本身定義的Scope。下面的例子演示了一個與線程綁定的Scope例子。
1 /**
2 * $Id: ThreadScopeDemo.java 90 2009-12-25 08:12:21Z xylz $
3 * xylz study project (www.imxylz.cn)
4 */
5 package cn.imxylz.study.guice.inject.more;
6
7 import com.google.inject.Binder;
8 import com.google.inject.Guice;
9 import com.google.inject.Injector;
10 import com.google.inject.Key;
11 import com.google.inject.Module;
12 import com.google.inject.Provider;
13 import com.google.inject.Scope;
14
15 /** a demo with thread-scope
16 * @author xylz (www.imxylz.cn)
17 * @version $Rev: 90 $
18 */
19 public class ThreadScopeDemo {
20
21 static class ThreadServiceScope implements Scope {
22
23 static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
24
25 @Override
26 public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
27 return new Provider<T>() {
28 @Override
29 public T get() {
30 T instance = (T) threadLocal.get();
31 if (instance == null) {
32 instance = unscoped.get();
33 threadLocal.set(instance);
34 }
35 return instance;
36 }
37 };
38 }
39
40 @Override
41 public String toString() {
42 return "Scopes.ThreadServiceScope";
43 }
44 }
45
46 public static void main(String[] args) {
47 final Injector inj=Guice.createInjector(new Module() {
48 @Override
49 public void configure(Binder binder) {
50 binder.bind(Service.class).to(WwwService.class).in(new ThreadServiceScope());
51 }
52 });
53 for(int i=0;i<3;i++) {
54 new Thread("Thread-"+i) {
55 public void run() {
56 for(int m=0;m<3;m++) {
57 System.out.println(String.format("%s-%d:%d",//
58 getName()//
59 ,m//
60 ,inj.getInstance(Service.class).hashCode()));
61 try {
62 Thread.sleep(50L);
63 } catch (Exception e) {
64 }
65 }
66 }
67 }.start();
68 }
69 }
70 }
71
注意,這裏用到了《Google Guice 入門教程03 - 依賴注入》的中的兩個類Service和WwwService。在本例中ThreadServiceScope 類是一個與線程綁定的做用域(利用ThreadLocal特性),噹噹前線程中沒有構造一個對象的時候先構造一個出來,而後放入線程上下文中,之後每次都 從線程中獲取對象。第50行是將WwwService服務以ThreadServiceScope的做用域綁定到Service服務上。第57-60行輸 出當前對象的hashCode,若是此類是同一對象的話就應該輸出相同的hashCode。爲了看到效果,咱們使用3個線程,每一個線程輸出三次來看結果。
Thread-0-0:18303751
Thread-1-0:23473608
Thread-2-0:21480956
Thread-1-1:23473608
Thread-0-1:18303751
Thread-2-1:21480956
Thread-1-2:23473608
Thread-2-2:21480956
Thread-0-2:18303751
咱們看到對於同一個線程(好比說Thread-0)的三次都輸出了相同的對象(hashCode爲18303751),而與線程2和線程3的hashCode不一樣。
(特別說明:若是兩個線程輸出了同一個hashCode沒必要驚慌,那是由於可能前一個線程生成的對象的地址空間被GC釋放了,結果下一個線程使用了上一個線程的相同空間,因此這裏使用Thread.sleep來下降這種可能性)
事實上在guice-servlet-2.0.jar中有與request和session綁定的scope。
com.google.inject.servlet.ServletScopes.REQUEST
com.google.inject.servlet.ServletScopes.SESSION
1.3.6 Eagerly Loading Bindings (當即初始化)
除了能夠綁定scope外,對象默認在第一次調用時被建立,也即所謂的延時加載,Guice也容許對象在注入到Guice容器中時就被建立出來(顯然這是針對單例模式纔有效)。
1 public class EagerSingletonDemo {
2
3 public EagerSingletonDemo() {
4 System.out.println(" constuctor:"+System.nanoTime());
5 }
6 void doit() {
7 System.out.println(" doit:"+System.nanoTime());
8 }
9 public static void main(String[] args) throws Exception{
10 Injector inj = Guice.createInjector(new Module() {
11 @Override
12 public void configure(Binder binder) {
13 binder.bind(EagerSingletonDemo.class).asEagerSingleton();
14 }
15 });
16 System.out.println("before call:"+System.nanoTime());
17 Thread.sleep(100L);
18 inj.getInstance(EagerSingletonDemo.class).doit();
19 }
20 }
結果輸出以下:
constuctor:26996967388652
before call:26996967713635
doit:26997069993702
能夠看到咱們的對象在調用getInstance以前就已經被構造出來了。
1.3.7 Stages (運行階段)
Guice還有一個特效,能夠指定Guice運行模式來控制Guice的加載速度。在com.google.inject.Stage枚舉中提供了TOOL,DEVELOPMENT,PRODUCTION三種模式。
TOOL描述的是帶有IDE等插件的運行模式;DEVELOPMENT是指在開發階段只加載本身須要的功能(對於非當即初始化單例對象採用延後加載),這 樣來下降加載不須要功能的時間;而PRODUCTION模式是指徹底加載全部功能(對於單例對象採用當即加載方式),這樣能夠更早的發現問題,省得等須要 某些功能的時候才發現問題(要知道咱們某些功能可能須要特定的條件才能觸發)。
其實只有比較多的單例對象,而且單例對象構造比較耗時的狀況下才能有用。大部分狀況下這點性能可能都忽略不計了。
默認狀況下Guice採用DEVELOPMENT模式。
1.3.8 Optional Injection (選項注入 )
選項注入描述的是若是不能從Guice容器中注入一個對象,那麼可使用一個默認的對象。看下面的例子。
1 public class OptionalInjectionDemo {
2 @Inject(optional=true)
3 Service service = new WwwService();
4 public static void main(String[] args) {
5 Guice.createInjector(new Module() {
6 public void configure(Binder binder) {
7 //binder.bind(Service.class).to(HomeService.class);
8 }
9 }).getInstance(OptionalInjectionDemo.class).service.execute();
10 }
11 }
上述例子中第2行描述的是選項注入,若是不能從Guice容器中獲取一個Service服務那麼就使用默認的WwwService,不然就是用獲取的服務。若是將第7行註釋去掉咱們就能夠看到實際上調用的是HomeService服務了。性能