Locked History Actions

Diff for "guice/CustomScope"

Differences between revisions 1 and 2
Deletions are marked like this. Additions are marked like this.
Line 16: Line 16:
    public <T> Provider<T> scope(Key<T> key, Provider<T> provider) {     public <T> Provider<T> scope(Key<T> key, Provider<T> provider) {
    System.out.println("scope callled!");
Line 37: Line 38:
scope called!
Line 40: Line 42:
となる。この例ではスコープを使った意味が全くない。 となる。この例ではスコープを使った意味が全くない。ただし、「scope called!」が一行しか表示されていないことに注意。
Line 44: Line 46:
次に専用のアノテーション(SampleScoped)を登場させてみる。この例も前と同じで全く意味がない。
injector.getInstance(Person.class)するたびに異なるPersonが返される。
次に専用のアノテーション(SampleScoped)を登場させ、それをbindScope()でスコープオブジェクトにバインドする。
そして、スコープオブジェクトが返すプロバイダはただ一つのPersonオブジェクトを返すようにし
てみる。
しかし、injector.getInstance(Person.class)するたびに異なるPersonが返される。
Line 54: Line 57:
  /** スコープ用アノテーション */   
Line 61: Line 64:
    public <T> Provider<T> scope(Key<T> key, Provider<T> provider) {
      return provider;
    public <T> Provider<T> scope(Key<T> key, final Provider<T> provider) {
      System.out.println("scope called!");
      return new Provider<T>() {
        private T person;
        @SuppressWarnings("unchecked")
        public T get() {
          if (person == null) person = provider.get();
          return (T)person;
        }
      };
Line 68: Line 79:
  public static class Sample {
    @Inject Person person;
  }
  
Line 75: Line 82:
        // アノテーションをスコープにバインド
Line 79: Line 85:
    System.out.println(injector.getInstance(Sample.class).person);
    System.out.println(injector.getInstance(Sample.class).person);
    System.out.println(injector.getInstance(Person.class));
    System.out.println(injector.getInstance(Person.class)); 
Line 84: Line 90:
結果は
{{{
sample.Scope2$Person@fd54d6
sample.Scope2$Person@1ccb029
}}}
となる。「scope called!」が表示されていないことに注意。
なぜなら、SampleScopedというスコープアノテーションがスコープオブジェクトにバインドされているため、Personは無関係と判断されているからである。そこで、Personに@SampleScopedを付加する。
{{{
  @SampleScoped
  public static class Person {}
}}}
結果は、
{{{
scope called!
sample.Scope2$Person@443226
sample.Scope2$Person@443226
}}}
となり、「scope called!」が呼び出され、二度のgetInstance()で同じPersonオブジェクトが返されることがわかる。

カスタムスコープ

カスタムスコープの仕組みについてマニュアルに一応の説明があるが、これは非常にわかりづらい。 しかし、誤解を恐れずに一言で言えば、「その時の条件に応じたプロバイダを返すオブジェクト」と言えるかと思う。 この動きをつぶさに追ってみる。

単純な例

package sample;

import com.google.inject.*;

public class Scope1 { 
  
  public static final Scope SCOPE_OBJECT = new Scope() { 
    public <T> Provider<T> scope(Key<T> key, Provider<T> provider) {     
      System.out.println("scope callled!");  
      return provider;
    }
  };
    
  public static class Person {} 
  
  public static void main(String[] args) {     
    Injector injector = Guice.createInjector(new AbstractModule() { 
      protected void configure() { 
        bind(Person.class).in(SCOPE_OBJECT); 
      } 
    }); 
    System.out.println(injector.getInstance(Person.class)); 
    System.out.println(injector.getInstance(Person.class));    
  } 
}

これは非常に単純な例である。bind(Person.class).in(SCOPE_OBJECT)としているが、このSCOPE_OBJECTがこの場合はProvider<Person>を返すオブジェクト(スコープの実態)である。しかし、この例ではあまりスコープらしくない。 ともあれ、上記を実行すると、

scope called!
sample.Scope1$Person@1ccb029
sample.Scope1$Person@1415de6

となる。この例ではスコープを使った意味が全くない。ただし、「scope called!」が一行しか表示されていないことに注意。

アノテーションを使う例

次に専用のアノテーション(SampleScoped)を登場させ、それをbindScope()でスコープオブジェクトにバインドする。 そして、スコープオブジェクトが返すプロバイダはただ一つのPersonオブジェクトを返すようにしてみる。 しかし、injector.getInstance(Person.class)するたびに異なるPersonが返される。

package sample;

import java.lang.annotation.*;

import com.google.inject.*;

public class Scope2 { 
  
  @Target({ ElementType.TYPE })
  @Retention(RetentionPolicy.RUNTIME)
  @ScopeAnnotation
  public @interface SampleScoped {}
  
  public static final Scope SCOPE_OBJECT = new Scope() { 
    public <T> Provider<T> scope(Key<T> key, final Provider<T> provider) { 
      System.out.println("scope called!");      
      return new Provider<T>() {
        private T person;
        @SuppressWarnings("unchecked")
        public T get() {
          if (person == null) person = provider.get();
          return (T)person;
        }
      };
    }
  };
    
  public static class Person {} 
  
  public static void main(String[] args) {     
    Injector injector = Guice.createInjector(new AbstractModule() { 
      protected void configure() { 
        bindScope(SampleScoped.class, SCOPE_OBJECT);
      } 
    }); 
    System.out.println(injector.getInstance(Person.class)); 
    System.out.println(injector.getInstance(Person.class)); 
  } 
} 

結果は

sample.Scope2$Person@fd54d6
sample.Scope2$Person@1ccb029

となる。「scope called!」が表示されていないことに注意。 なぜなら、SampleScopedというスコープアノテーションがスコープオブジェクトにバインドされているため、Personは無関係と判断されているからである。そこで、Personに@SampleScopedを付加する。

  @SampleScoped
  public static class Person {} 

結果は、

scope called!
sample.Scope2$Person@443226
sample.Scope2$Person@443226

となり、「scope called!」が呼び出され、二度のgetInstance()で同じPersonオブジェクトが返されることがわかる。