Googel Guice注入方式

前端之家收集整理的这篇文章主要介绍了Googel Guice注入方式前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1.类依赖注入

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

HelloWorld.java
     public interface HelloWorld {
         String sayHello();
     }

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

public class HelloWorldImpl implements HelloWorld {
         @Override
         public String sayHello() {
            return "Hello,world!";
         }
     }

写一个测试例子看看,HelleWorldTest.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);
           hw.sayHello();
         }
     }

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

问题(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(HelloWorldImpl.class).to(HelloWorldSubImpl.class);
             }
         });
       HelloWorldImpl hw = inj.getInstance(HelloWorldImpl.class);
       System.out.println(hw.sayHello());

太好了,支持子类绑定,这样即使我们将一个实现类发布出去了(尽管不推荐这么做),我们在后期仍然有办法替换实现类。
使用bind有一个好处,由于JAVA 5以上的泛型在编译器就确定了,所以可以帮我们检测出绑定错误的问题,而这个在配置文件中是无法检测出来的。
这样看起来Module就像是一个Map,根据一个Key获取其Value,非常简单的逻辑。

问题(5),可以绑定到我们自己构造出来的实例么?
解答(5)当然可以!看下面的例子。

Injector inj=  Guice.createInjector(new Module() {
             @Override
             public void configure(Binder binder) {
                 binder.bind(HelloWorld.class).toInstance(new HelloWorldImpl());
             }
         });
       HelloWorld hw = inj.getInstance(HelloWorld.class);
       System.out.println(hw.sayHello());

问题(6),我不想自己提供逻辑来构造一个对象可以么?
解答(6),可以,Guice提供了一个方式(Provider),允许自己提供构造对象的方式。

Injector inj=  Guice.createInjector(new Module() {
       @Override
       public void configure(Binder binder) {
           binder.bind(HelloWorld.class).toProvider(new Provider<HelloWorld>() {
               @Override
               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();
HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
System.out.println(hw.sayHello());

问题(8),可以使用注解方式完成注入么?不想手动关联实现类。
解答(8),好,Guice提供了注解的方式完成关联。我们需要在接口上指明此接口被哪个实现类关联了。

@ImplementedBy(HelloWorldImpl.class)
     public interface HelloWorld { 
         String sayHello();
     } 

Injector inj=  Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.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 hw = inj.getInstance(HelloWorld.class);
      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!";
         }
     }

2. 属性注入(Field Inject)

2.1 基本属性注入
首先来看一个例子。Service.java

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

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

FieldInjectDemo.java

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看起来还不支持静态字段注入。

2.2 构造函数注入(Constructor Inject)
继续看例子。例子是说明问题的很好方式。

package cn.imxylz.study.guice.inject;
      import com.google.inject.Guice;
      import com.google.inject.Inject;
    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,而且写了的话不能通过编译。

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)。下面的例子演示了如何注入一个静态的字段。

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

参考:http://www.cnblogs.com/xd502djj/archive/2012/06/25/2561414.html

原文链接:https://www.f2er.com/javaschema/283639.html

猜你在找的设计模式相关文章