第 1 章 Hello World

假设大家都了解interface和实现类这些基础知识。

1.1. Module绑定

按照下面的步骤编写我们的第一个guice程序。

  1. 接口Service。

    public interface Service {
        void hello();
    }
                    

    实现接口的ServiceImpl。

    public class ServiceImpl implements Service{
        public void hello() {
            System.out.println("hello");
        }
    }
                    
  2. 有了接口和实现类,现在我们看看如何通过guice取得它们。

    import com.google.inject.*;
    
    public class Main {
        public static void main(String[] args) {
            Service service = Guice.createInjector(new Module(){
                public void configure(Binder binder) {
                    binder.bind(Service.class).to(ServiceImpl.class);
                }
            }).getInstance(Service.class);
    
            service.hello();
        }
    }
                    

    注意一下new Module() {}部分,创建一个匿名类继承Module接口,这个接口里的configure()方法把Service.class和ServiceImpl.class绑定在一起,以后谁需要Service.class的时候,guice就会自动生成一个ServiceImpl.class放到需要的地方。

    看到了吗?拜java强类型所赐,Service.class和ServiceImpl.class会在编译期间先检测一遍,省得你把不相干的两个东西放到一起。

    把刚刚配置的这个Module作为参数交给Guice.createInjector(),这个方法获得的Injector对象就是我们贴近guice的大门,不管需要什么都可以向它索取。getInstance(Service.class)会根据Module中的绑定返回ServiceImpl.class的一个实例,调用hello.service()或者做什么都可以啦。

例子:01-01。

1.2. 注解绑定

懒惰是程序员最大的美德。有没有不用写Module也可以让guice将Service.class和ServiceImpl.class自动关联起来的方法?让Main.class就像下面这样。

import com.google.inject.*;

public class Main {
    public static void main(String[] args) {
        Service service = Guice.createInjector().getInstance(Service.class);

        service.hello();
    }
}
        

不用去写Module手工绑定Service.class和ServiceImpl.class,也能正确获得ServiceImpl.class类型的实例。

可惜完全不配置也是不可能的,为了实现自动绑定的功能我们要在Service.class中添加注解。

import com.google.inject.*;

@ImplementedBy(ServiceImpl.class)
public interface Service {
    void hello();
}
        

@ImplementedBy,告诉guice在所有需要Service.class的地方都放上ServiceImpl.class的实例。

有了这个注解,guice生成的Injector就能根据它的配置去查找ServiceImpl.class了,但是guice-1.0里还有漏洞,如果ServiceImpl.class没有实现Service.class接口,编译期间也无法检测出来,从这点来看还是使用Module比较保险。

例子:01-02。

1.3. 讨论一下

有人会说,你这样把接口与实现类的关系直接硬编码到Service.java中有什么意义?如果是像spring里一样写到xml中以后升级的时候不用编译类,直接修改xml再重新部署就好了。

那我就这样说,实际工作中有可能频繁修改Service的实现吗?就算修改了,我只要改一下Service.java中注解的指向再编译就可以了,既然都要重新部署,编译一下也无关大雅吧?再说总要进行测试的,有人敢直接替换实现类吗?

估计又有人说了,那既然接口和实现类完全绑定,那为什么还要这么麻烦定义一个接口呢?直接写成实现类不就好了?

第一,接口是为了进行契约式编程,我们为其他组提供的接口可以约束双方使用的方法入口和参数形式。

第二,使用接口也更容易实现测试。

当然,如果你不愿意用接口也好,直接使用类的话,我们不必写任何注解guice也能自动找到需要的类。

ServiceImpl.java

public class ServiceImpl {
    public void hello() {
        System.out.println("hello");
    }
}
        

Main.java

import com.google.inject.*;

public class Main {
    public static void main(String[] args) {
        ServiceImpl service = Guice.createInjector().getInstance(ServiceImpl.class);

        service.hello();
    }
}
        

例子:01-03。