天天看点

Guice框架-DI(依赖注入基础入门)

所谓的绑定就是将一个接口绑定到具体的类中,这样客户端不用关心具体的实现,而只需要获取相应的接口完成其服务即可。

helloworld.java

     public interface helloworld {

        string sayhello();

     }

然后是具体的实现,helloworldimpl.java

     public class helloworldimpl implements helloworld {

        @override

       public string sayhello() {

             return "hello, world!";

         }

写一个测试例子看看,helloworldtest.java

   public class helleworldtest {

        @test

       public void testsayhello() {

         injector inj=  guice.createinjector(new module() {

                @override

                public void configure(binder binder) {

                  binder.bind(helloworld.class).to(helloworldimpl.class);

                }

            });

           helloworld hw = inj.getinstance(helloworld.class);

           assert.assertequals(hw.sayhello(), "hello, world!");

       }

   }

这个例子非常简单,通俗的将就是将一个helloworldimpl的实例与helloworld关联起来,当想guice获取一个helloworld实例的时候,guice就返回一个helloworldimpl的实例,然后我们就可以调用helloworld服务的方法了。

自我分析:针对于factory工厂类的框架整合!

问题(1)helloworld是单例的么?测试下。

helloworld hw = inj.getinstance(helloworld.class); 

assert.assertequals(hw.sayhello(), "hello, world!");

helloworld hw2 = inj.getinstance(helloworld.class);

system.out.println(hw.hashcode()+"->"+hw2.hashcode());

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),如果绑定多个实现到同一个接口上会出现什么情况?

public class helloworldimplagain implements helloworld {

    @override

     public string sayhello() {

        return "hello world again.";

}

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),可以绑定一个实现类到实现类么?

injector inj=  guice.createinjector(new module() {

      @override

       public void configure(binder binder) {

          binder.bind(helloworldimpl.class).to(helloworldimpl.class);

  });

helloworld hw = inj.getinstance(helloworldimpl.class);

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);

也就是说只能绑定一个类的子类到其本身

。改造下,改用子类替代。

    public class helloworldsubimpl extends helloworldimpl {

         @override

         public string sayhello() {

             return "@helloworldsubimpl";

    }

   injector inj = guice.createinjector(new module() {

            @override

           public void configure(binder binder) {

               binder.bind(helloworldsubimpl.class).to(helloworldsubimpl.class);

           }

        });

      helloworldimpl hw = inj.getinstance(helloworldimpl.class);

      system.out.println(hw.sayhello());

支持子类绑定,这样即使我们将一个实现类发布出去了(尽管不推荐这么做),我们在后期仍然有办法替换实现类。

使用bind有一个好处,由于java 5以上的泛型在编译器就确定了,所以可以帮我们检测出绑定错误的问题,而这个在配置文件中是无法检测出来的。

这样看起来module像是一个map,根据一个key获取其value,非常简单的逻辑。

问题(5),可以绑定到我们自己构造出来的实例么?

解答(5)当然可以!看下面的例子。

             @override

             public void configure(binder binder) {

                binder.bind(helloworld.class).toinstance(new helloworldimpl());

             }

         });

      helloworld hw = inj.getinstance(helloworld.class);

问题(6),我不想自己提供逻辑来构造一个对象可以么?

解答(6),可以guice提供了一个方式(provider<t>),允许自己提供构造对象的方式。

  injector inj=  guice.createinjector(new module() {

        public void configure(binder binder) {

            binder.bind(helloworld.class).toprovider(new provider<helloworld>() {

                public helloworld get() {

                    return new helloworldimpl();

   });

 helloworld hw = inj.getinstance(helloworld.class);

 system.out.println(hw.sayhello());

问题(7),实现类可以不经过绑定就获取么?比如我想获取helloworldimpl的实例而不通过module绑定么?

解答(7),可以,实际上guice能够自动寻找实现类。

injector inj=  guice.createinjector();

问题(8),可以使用注解方式完成注入么?不想手动关联实现类。

解答(8),好,guice提供了注解的方式完成关联。我们需要在接口上指明此接口被哪个实现类关联了。

 @implementedby(helloworldimpl.class)

   public interface helloworld {

         string sayhello();

  }

事实上对于一个已经被注解的接口我们仍然可以使用module来关联,这样获取的实例将是module关联的实例,而不是@implementedby注解关联的实例。这样仍然遵循一个原则,手动优于自动。

问题(9)再回头看问题(1)怎么绑定一个单例?

      injector inj = guice.createinjector(new module() {

          @override

          public void configure(binder binder) {

           binder.bind(helloworld.class).to(helloworldimplagain.class).in(scopes.singleton);

          }

      });

      helloworld hw2 = inj.getinstance(helloworld.class);

      system.out.println(hw.hashcode() + "->" + hw2.hashcode());

可以看到现在获取的实例已经是单例的,不再每次请求生成一个新的实例。

事实上guice提供两种scope,

com.google.inject.scopes.singleton和com.google.inject.scopes.no_scope,

所谓没有scope即是每次生成一个新的实例。

对于自动注入就非常简单了,只需要在实现类加一个singleton注解即可。

@singleton

public class helloworldimpl implements helloworld {

@override

  public string sayhello() {

    return "hello, world!";