本章节继续讨论依赖注入的其他话题,包括作用域(scope,这里有一个与线程绑定的作用域例子)、立即初始化(Eagerly Loading Bindings)、运行阶段(Stage)、选项注入(Optional Injection)等等。 

1.3.5 Scope(作用域)

在1.1章节中我们初步了解了对象的单例模式,在Guice中提供了一些常见的作用域,比如对于单例模式有下面两个作用域。

 

com.google.inject.Scopes.SINGLETON

    com.google.inject.Scopes.NO_SCOPE


在使用上,可以使用Module的bind来实现,看下面的例子。

 



1 
      
  public
   
  class
   ScopeDemo {

   2 
          
  public
   
  static
   
  void
   main(String[] args) {

   3 
  

   4 
              Service service 
  =
   Guice.createInjector(
  new
   Module() {

   5 
                  @Override

   6 
                  
  public
   
  void
   configure(Binder binder) {

   7 
                      binder.bind(Service.
  class
  ).to(WwwService.
  class
  ).in(Scopes.SINGLETON);

   8 
                  }

   9 
              }).getInstance(Service.
  class
  );

  10 
              service.execute();

  11 
          }

  12 
      }

  13 
  

  14



当然单例模式还可以似乎用@Singleton注解。

在com.google.inject.binder.ScopedBindingBuilder.in(Scope)方法中,一个Scope除了可以使上面的SINGLETION和NO_SCOPE外,还可以是自己定义的Scope。下面的例子演示了一个与线程绑定的Scope例子。



1 
  /**
  

   2 
   * $Id: ThreadScopeDemo.java 90 2009-12-25 08:12:21Z xylz $

   3 
   * xylz study project

   4 
   
  */
  

   5 
  package
   cn.imxylz.study.guice.inject.more;

   6 
  

   7 
  import
   com.google.inject.Binder;

   8 
  import
   com.google.inject.Guice;

   9 
  import
   com.google.inject.Injector;

  10 
  import
   com.google.inject.Key;

  11 
  import
   com.google.inject.Module;

  12 
  import
   com.google.inject.Provider;

  13 
  import
   com.google.inject.Scope;

  14 
  

  15 
  /**
   a demo with thread-scope

  16 
   * 
  @author
   

  17 
   * 
  @version
   $Rev: 90 $

  18 
   
  */
  

  19 
  public
   
  class
   ThreadScopeDemo {

  20 
  

  21 
      
  static
   
  class
   ThreadServiceScope 
  implements
   Scope {

  22 
  

  23 
          
  static
   ThreadLocal
  <
  Object
  >
   threadLocal 
  =
   
  new
   ThreadLocal
  <
  Object
  >
  ();

  24 
  

  25 
          @Override

  26 
          
  public
   
  <
  T
  >
   Provider
  <
  T
  >
   scope(
  final
   Key
  <
  T
  >
   key, 
  final
   Provider
  <
  T
  >
   unscoped) {

  27 
              
  return
   
  new
   Provider
  <
  T
  >
  () {

  28 
                  @Override

  29 
                  
  public
   T get() {

  30 
                      T instance 
  =
   (T) threadLocal.get();

  31 
                      
  if
   (instance 
  ==
   
  null
  ) {

  32 
                          instance 
  =
   unscoped.get();

  33 
                          threadLocal.set(instance);

  34 
                      }

  35 
                      
  return
   instance;

  36 
                  }

  37 
              };

  38 
          }

  39 
  

  40 
          @Override

  41 
          
  public
   String toString() {

  42 
              
  return
   
  "
  Scopes.ThreadServiceScope
  "
  ;

  43 
          }

  44 
      }

  45 
      

  46 
      
  public
   
  static
   
  void
   main(String[] args) {

  47 
          
  final
   Injector inj
  =
  Guice.createInjector(
  new
   Module() {

  48 
              @Override

  49 
              
  public
   
  void
   configure(Binder binder) {

  50 
                  binder.bind(Service.
  class
  ).to(WwwService.
  class
  ).in(
  new
   ThreadServiceScope());

  51 
              }

  52 
          });

  53 
          
  for
  (
  int
   i
  =
  0
  ;i
  <
  3
  ;i
  ++
  ) {

  54 
              
  new
   Thread(
  "
  Thread-
  "
  +
  i) {

  55 
                  
  public
   
  void
   run() {

  56 
                      
  for
  (
  int
   m
  =
  0
  ;m
  <3
  ;m
  ++
  ) {

  57 
                          System.out.println(String.format(
  "
  %s-%d:%d
  "
  ,
  //

  58 
                                  getName()
  //

  59 
                                  ,m
  //

  60 
                                  ,inj.getInstance(Service.
  class
  ).hashCode()));

  61 
                          
  try
   {

  62 
                              Thread.sleep(
  50L
  );

  63 
                          } 
  catch
   (Exception e) {

  64 
                          }

  65 
                      }

  66 
                  }

  67 
              }.start();

  68 
          }

  69 
      }

  70 
  }

  71



 

注意,这里用到了《Google Guice 入门教程03 - 依赖注入》的中的两个类Service和WwwService。在本例中ThreadServiceScope类是一个与线程绑定的作用域(利用ThreadLocal特性),当当前线程中没有构造一个对象的时候先构造一个出来,然后放入线程上下文中,以后每次都从线程中获取对象。第50行是将WwwService服务以ThreadServiceScope的作用域绑定到Service服务上。第57-60行输出当前对象的hashCode,如果此类是同一对象的话就应该输出相同的hashCode。为了看到效果,我们使用3个线程,每个线程输出三次来看结果。



Thread-0-0:18303751
  
Thread-1-0:23473608
  
Thread-2-0:21480956
  
Thread-1-1:23473608
  
Thread-0-1:18303751
  
Thread-2-1:21480956
  
Thread-1-2:23473608
  
Thread-2-2:21480956
  
Thread-0-2:18303751


 

我们看到对于同一个线程(比如说Thread-0)的三次都输出了相同的对象(hashCode为18303751),而与线程2和线程3的hashCode不同。

(特别说明:如果两个线程输出了同一个hashCode不必惊慌,那是因为可能前一个线程生成的对象的地址空间被GC释放了,结果下一个线程使用了上一个线程的相同空间,所以这里使用Thread.sleep来降低这种可能性)

事实上在guice-servlet-2.0.jar中有与request和session绑定的scope。

com.google.inject.servlet.ServletScopes.REQUEST
com.google.inject.servlet.ServletScopes.SESSION


 

1.3.6 Eagerly Loading Bindings (立即初始化)

除了可以绑定scope外,对象默认在第一次调用时被创建,也即所谓的延时加载,Guice也允许对象在注入到Guice容器中时就被创建出来(显然这是针对单例模式才有效)。


1 
  public
   
  class
   EagerSingletonDemo {

   2 
  

   3 
      
  public
   EagerSingletonDemo() {

   4 
          System.out.println(
  "
   constuctor:
  "
  +
  System.nanoTime());

   5 
      }

   6 
      
  void
   doit() {

   7 
          System.out.println(
  "
         doit:
  "
  +
  System.nanoTime());

   8 
      }

   9 
      
  public
   
  static
   
  void
   main(String[] args) 
  throws
   Exception{

  10 
          Injector inj 
  =
   Guice.createInjector(
  new
   Module() {

  11 
              @Override

  12 
              
  public
   
  void
   configure(Binder binder) {

  13 
                  binder.bind(EagerSingletonDemo.
  class
  ).asEagerSingleton();

  14 
              }

  15 
          });

  16 
          System.out.println(
  "
  before call:
  "
  +
  System.nanoTime());

  17 
          Thread.sleep(
  100L
  );

  18 
          inj.getInstance(EagerSingletonDemo.
  class
  ).doit();

  19 
      }

  20 
  }


 

结果输出如下:



constuctor:
  26996967388652
  
before call:
  26996967713635
  
       doit:
  26997069993702



 

可以看到我们的对象在调用getInstance之前就已经被构造出来了。

1.3.7 Stages (运行阶段)

Guice还有一个特效,可以指定Guice运行模式来控制Guice的加载速度。在com.google.inject.Stage枚举中提供了TOOL,DEVELOPMENT,PRODUCTION三种模式。

TOOL描述的是带有IDE等插件的运行模式;DEVELOPMENT是指在开发阶段只加载自己需要的功能(对于非立即初始化单例对象采用延后加载),这样来降低加载不需要功能的时间;而PRODUCTION模式是指完全加载所有功能(对于单例对象采用立即加载方式),这样可以更早的发现问题,免得等需要某些功能的时候才发现问题(要知道我们某些功能可能需要特定的条件才能触发)。

其实只有比较多的单例对象,并且单例对象构造比较耗时的情况下才能有用。大部分情况下这点性能可能都忽略不计了。

默认情况下Guice采用DEVELOPMENT模式。

 

1.3.8 Optional Injection (选项注入 )

选项注入描述的是如果不能从Guice容器中注入一个对象,那么可以使用一个默认的对象。看下面的例子。



1 
  public
   
  class
   OptionalInjectionDemo {

   2 
      @Inject(optional
  =
  true
  )

   3 
      Service service 
  =
   
  new
   WwwService();

   4 
      
  public
   
  static
   
  void
   main(String[] args) {

   5 
          Guice.createInjector(
  new
   Module() {

   6 
              
  public
   
  void
   configure(Binder binder) {

   7 
                  
  //
  binder.bind(Service.class).to(HomeService.class);
  

   8 
              }

   9 
          }).getInstance(OptionalInjectionDemo.
  class
  ).service.execute();

  10 
      }

  11 
  }



 

上述例子中第2行描述的是选项注入,如果不能从Guice容器中获取一个Service服务那么就使用默认的WwwService,否则就是用获取的服务。如果将第7行注释去掉我们就可以看到实际上调用的是HomeService服务了。

 

到此为止,Guice依赖注入的基本教程就学习完了,下面的章节我们进入经典的AOP教程学习。