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 Teny (www.teny32.blog.51cto.com)."); 5 } 6 } FieldInjectDemo.java 1 /** a demo with Field inject 2 * @author Teny (www.teny32.blog.51cto.com) 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 Teny (www.teny32.blog.51cto.com) 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