sexta-feira, 25 de janeiro de 2008

Criando um Scope ThreadLocal no Guice

Eu comecei a estudar um pouco o Guice, que é um framework da google de injeção de dependência. Estava fazendo uns testes e queria ter uma forma de que o Guice me retornasse sempre a mesma instância quando solicitar um objeto na mesma thread, algo como se feito com esse Factory:

public class ServiceFactory {

 private static ThreadLocal<Service> tl = new ThreadLocal<Service>();

 public static Service get() {
  Service t = tl.get();
  if (t == null) {
   t = new ServiceImpl();
   tl.set(t);
  }
  return t;
 }
}
Porém não achei um scope para isso (na real procurei bem pouco). Resolvi, até para aprendizado criar um novo scope para que seja ThreadLocal, até que deu certo, eis o resultado:
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
...

class LocalThreadScope implements Scope {
 public <T> Provider<T> scope(Key<T> key, final Provider<T> provider) {
  return new Provider<T>() {
   ThreadLocal<T> tl = new ThreadLocal<T>();

   public T get() {
    T t = tl.get();
    if (t == null) {
     t = provider.get();
     tl.set(t);
    }
    return t;
   }
  };
 }

 @Override
 public String toString() {
  return "ThreadLocalScope";
 }
}
Usando-se esse Scope para fazer o bind, o Guice ira criar um novo objeto para cada thread diferente. Exemplo:
public interface Service {
 void go();
}

public class ServiceImpl implements Service {
 public void go() {
  System.out.println(this + " - " + Thread.currentThread());
 }
}

public class MyModule implements Module {
 public void configure(Binder binder) {
  binder.bind(Service.class).to(ServiceImpl.class).in(
    new LocalThreadScope());
 }
}


public class Main {
 public static void main(String[] args) {
  final Injector inj = Guice.createInjector(new MyModule());
  Service s = inj.getInstance(Service.class);
  s.go();
  s = inj.getInstance(Service.class);
  s.go();
  Runnable r1 = new Runnable() {
   public void run() {
    Service s = inj.getInstance(Service.class);
    s.go();
   }
  };
  new Thread(r1).start();
 }
}