【转】guice 入门教程:依赖注入之属性注入

首先来看一个例子。

 @ImplementedBy(ServiceImpl.class)
 public interface Service {
     void execute();
 }
public class ServiceImpl implements Service {
    @Override
    public void execute() {
        System.out.println("This is made by imxylz (www.imxylz.cn).");
    }
}

public class FieldInjectDemo {
    @Inject
    private Service servcie;
    public Service getServcie() {
        return servcie;
    }
    public static void main(String[] args) {
        FieldInjectDemo demo = Guice.createInjector().getInstance(FieldInjectDemo.class);
        demo.getServcie().execute();
    }
}

这个例子比较简单。具体来说就是将接口Service通过@Inject注解注入到FieldInjectDemo类中,然后再FieldInjectDemo类中使用此服务而已。当然Service服务已经通过@ImplementedBy注解关联到ServiceImpl 类中,每次生成一个新的实例(非单例)。注意,这里FieldInjectDemo类没有通过Module等关联到Guice中,具体可以查看《》。

意料之中得到了我们期待的结果。

同样,我们通过问答的方式来加深理解(注意,入门教程我们只是强调怎么使用,至于原理和底层的思想我们放到高级教程中再谈)。

问题(1):可以自己构造FieldInjectDemo 对象而不通过Guice么?

public class FieldInjectDemo2 {
    @Inject
    private Service servcie;
    public Service getServcie() {
        return servcie;
    }
    public static void main(String[] args) {
        FieldInjectDemo2 fd = new FieldInjectDemo2();
        fd.getServcie().execute();
    }
}

就像上面的例子中一样,然后运行下看看?非常不幸,我们得到了一个谁都不喜欢的结果。


Exception in thread "main" java.lang.NullPointerException

    at cn.imxylz.study.guice.inject.FieldInjectDemo2.main(FieldInjectDemo2.java:22)

很显然,由于FieldInjectDemo2不属于Guice容器(暂且称为容器吧)托管,这样Service服务没有机会被注入到FieldInjectDemo2类中。

问题(2):可以注入静态属性么?

看下面的代码。

public class FieldInjectDemo2 {
    @Inject
    private static Service servcie;
    public static Service getServcie() {
        return servcie;
    }
    public static void main(String[] args) {
        FieldInjectDemo2 fd = Guice.createInjector().getInstance(FieldInjectDemo2.class);
        FieldInjectDemo2.getServcie().execute();
    }
}

很不幸!运行结果告诉我们Guice看起来还不支持静态字段注入。

好了,上面两个问题我们暂且放下,我们继续学习其它注入功能。

1.2.2 构造函数注入(Constructor Inject)

继续看例子。例子是说明问题的很好方式。

package cn.imxylz.study.guice.inject;
import com.google.inject.Guice;
import com.google.inject.Inject;
/** a demo with constructor inject
 * @author xylz (www.imxylz.cn)
 * @version $Rev: 75 $
 */
public class ConstructorInjectDemo {
    private Service service;
    @Inject
    public ConstructorInjectDemo(Service service) {
        this.service=service;
    }
    public Service getService() {
        return service;
    }
    public static void main(String[] args) {
        ConstructorInjectDemo cid =      Guice.createInjector().getInstance(ConstructorInjectDemo.class);
        cid.getService().execute();
    }
}

我们在构造函数上添加@Inject来达到自动注入的目的。构造函数注入的好处是可以保证只有一个地方来完成属性注入,这样可以确保在构造函数中完成一些初始化工作(尽管不推荐这么做)。当然构造函数注入的缺点是类的实例化与参数绑定了,限制了实例化类的方式。

问题(3):构造函数中可以自动注入多个参数么?

public class ConstructorInjectDemo {
    private Service service;
    private HelloWorld helloWorld;
    @Inject
    public ConstructorInjectDemo(Service service,HelloWorld helloWorld) {
        this.service=service;
        this.helloWorld=helloWorld;
    }
    public Service getService() {
        return service;
    }
    public HelloWorld getHelloWorld() {
        return helloWorld;
    }
    public static void main(String[] args) {
        ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
        cid.getService().execute();
        System.out.println(cid.getHelloWorld().sayHello());
    }
}

非常完美的支持了多参数构造函数注入。当然了没有必要写多个@Inject,而且写了的话不能通过编译。

1.2.3 Setter注入(Setter Method Inject)

有了上面的基础我们再来看Setter注入就非常简单了,只不过在setter方法上增加一个@Inject注解而已。

public class SetterInjectDemo {
    private Service service;
    @Inject
    public void setService(Service service) {
        this.service = service;
    }
    public Service getService() {
        return service;
    }
    public static void main(String[] args) {
        SetterInjectDemo sid = Guice.createInjector().getInstance(SetterInjectDemo.class);
        sid.getService().execute();
    }
}


好了我们再回头看问题2的静态注入(static inject)。下面的例子演示了如何注入一个静态的字段。

/** a demo for static field inject
 * @author xylz (www.imxylz.cn)
 * @version $Rev: 78 $
 */
public class StaticFieldInjectDemo {
    @Inject
    private static Service service;
    public static void main(String[] args) {
        Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.requestStaticInjection(StaticFieldInjectDemo.class);
            }
        });
        StaticFieldInjectDemo.service.execute();
    }
}

非常棒!上面我们并没有使用Guice获取一个StaticFieldInjectDemo实例(废话),实际上static字段(属性)是类相关的,因此我们需要请求静态注入服务。但是一个好处是在外面看起来我们的服务没有Guice绑定,甚至client不知道(或者不关心)服务的注入过程。

再回到问题(1),参考上面静态注入的过程,我们可以使用下面的方式来注入实例变量的属性。

public class InstanceFieldInjectDemo {
    @Inject
    private Service service;
    public static void main(String[] args) {
       final InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
        Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.requestInjection(ifid);
            }
        });
        ifid.service.execute();
    }
}

实际上这里有一种简便的方法来注入字段,实际上此方法也支持Setter注入。

public class InstanceFieldInjectDemo {
    @Inject
    private Service service;
    public static void main(String[] args) {
        InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
        Guice.createInjector().injectMembers(ifid);
        ifid.service.execute();
    }
}