Пользовательские прицелы Dagger, как это сделать?

Как создавать собственные области видимости с помощью Dagger?

Есть ли рекомендации? Я не нашел их.

Я разрабатываю приложение Vaadin, и мне понадобится настраиваемая область. Что-то вроде UiScoped.

с уважением


person Jako    schedule 04.06.2013    source источник


Ответы (1)


Dagger не использует тот же механизм, что и Guice. Dagger, в частности, не прозрачно обрабатывает область действия, как это делает Guice, с различными аннотациями области действия, одним инжектором и различными кэшами экземпляров за кулисами. Вместо этого он использует два принципа. Во-первых, @Singleton означает «один на граф» (самая строгая интерпретация JSR-330), а во-вторых, что графы могут быть связаны в иерархию.

Dagger использует это дерево иерархически связанных графов, где вы создаете граф, добавляя дополнительные модули и расширяя его с помощью метода plus(), чтобы создать граф с «ограниченной областью», который может иметь более короткое время жизни. Это похоже на Child Injectors в guice. Важным принципом здесь является то, что экземпляры в расширенном графе могут видеть экземпляры в исходном графе, но не наоборот. Таким образом, концентрическая природа более короткого времени жизни отражается в видимости — короткоживущий объект может видеть (зависеть) от более долгоживущего объекта, но не наоборот. Таким образом, объект, который живет в течение жизни запроса, может видеть объект, который живет в течение жизни приложения, но не наоборот.

Ожидается, что именно с помощью этого механизма можно более узко охватывать кэшированные экземпляры.

Если кто-то настраивает граф с некоторыми модулями и есть синглтон, у него будет один экземпляр, кэшированный в этом графе, который будет предоставлен всем зависимым объектам. Если создать расширение для этого графа с помощью метода plus(), настроив его с другими модулями, которые содержат аннотированные привязки @Singleton, то эти другие модули будут по одному на граф... но по одному на экземпляр более короткого живой экземпляр ObjectGraph.

Например, давайте смоделируем сервер, который отвечает на запросы, где нам нужны некоторые объекты, которые живут в течение жизни приложения, и некоторые объекты, которые живут только в течение более короткого времени жизни запроса:

@Module()
public class MyAppModule {
  @Provides ConnectionDictonary connectionDictionary() {
    return new ConnectionDictonary(System.getProperty("some.property"));
  }

  /** Stateless mockable utilities for this app */
  @Provides Util util() { new Util(); }

  @Provides @Singleton DataStore store() { 
    return new DataStore();
  }

  @Provides @Singleton ConnectionPool pool(DataStore store, ConnectionDictionary dict) { 
    try {
      return DataStore.connectionPool(dict, System.getProperty("pool.size"));
    } catch (Exception e) {
      // bad bad bad
      throw new RuntimeException("Could not connect to datastore.", e);
    }
  }

}

// This module "adds to" MyAppModule by adding additional graph elements in
// an extended graph.
@Module(injects=MyRequestEndpoint.class, addsTo = MyAppModule.class)
public class MyRequestModule {
  private Request req;
  public MyRequestModule(Request req) { this.req = req; }

  @Provides @Singleton RequestObject request() { return req; }

  @Provides @Singleton User user(ConnectionPool pool, Request req, Util util) {
    try {
      Connection conn = pool.obtain();
      // getUser cannot throw null;
      return util.getUser(conn, req.get("user.id"), Crypto.hash(req.get("pass")));
    } catch (UserNotFoundException e) {
      return User.UNKNOWN;
    } catch (Exception e) {
      throw new RuntimeException("Could not obtain a user.", e);
    } finally { 
      // TODO: try-with-resources in Java7
      pool.release();
    }
  }

}

public class MyRequestEndpoint {
  @Inject ConnectionPool pool;
  @Inject Request req;

  public Output performService() {
    try {
      Connection conn = pool.obtain();
      // ... does stuff with request
    } finally {
      conn.release();
    }
  }
}

public class MyApp {    
  public void main(String ... args) {
    graph = ObjectGraph.create(MyAppModule.class);
    new ServiceListener(graph).start(); 
  }
}

public ServiceListener {
  private final ObjectGraph appGraph;
  public ServiceListener(ObjectGraph appGraph) {
    this.appGraph = appGraph;
  }

  //... infrastructure for listening and building request/response objects, etc.

  public void serveRequest(Request req, Response res) {
    // Take the application-scoped graph and create another graph we will 
    // use in this request and throw away.
    ObjectGraph requestGraph = MyApp.graph().plus(new MyRequestModule(req));
    Output output = requestGraph.get(MyRequestEndpoint.class).performService();
    Util.populateResult(output, result);
    result.flush();
  }
}

В этом примере каждая MyRequestEndpoint получит общий экземпляр ConnectionPool, но конечная точка в любых двух запросах получит два разных RequestObject.

Это несколько глупый пример, построенный на основе шаблона J2EE. Что-то настолько тривиальное, что вы бы не структурировали таким образом, и вам понадобятся более прочные леса для правильной модели сервера. Действительно, проект Dagger, скорее всего, сделает это (хотя я с уважением рекомендую использовать внедренные объекты службы и один диспетчерский сервлет или фильтр).

Но мы надеемся, что это иллюстрирует более узкую область применения знакомой модели.

Ключ не в аннотации, а во времени жизни графиков. Вы создаете более короткий граф как «дочерний элемент» или «расширение» долгоживущего графа. Объекты, запоминаемые на этих графиках, имеют время жизни (или области действия) объектов управления графиком.

person Christian Gruber    schedule 04.06.2013
comment
Кстати, Dagger в настоящее время не совместим с GWT, хотя мы возлагаем большие надежды. Мы ожидаем, что будет доступен подход в стиле Ginjector, который мог бы работать в GWT, но сейчас он имеет более низкий приоритет, чем другие проблемы. Таким образом, вы не можете использовать его на стороне клиента. - person Christian Gruber; 05.06.2013
comment
Точнее, вы можете использовать Dagger с GWT через Sheath, но это будет намного медленнее, чем GIN (кстати, который также не поддерживает области видимости). - person Thomas Broyer; 05.06.2013
comment
Спасибо @ChristianGruber за такую ​​хорошую работу по предоставлению более подробной информации о проекте Dagger. Что касается примера, я думаю, это должно быть s/MyApp.graph()/this.appGraph/ и s/@Inject RequestObject req;/@Inject RequestObject req;/, просто убедитесь, что я не ошибаюсь. - person David Santiago Turiño; 24.09.2013
comment
Я не вижу, где MySingleton внедряется или вообще используется в этом примере, кроме того, что на него нацелен модуль? - person Matthias; 15.11.2013
comment
Упс. Я думаю, что это была ошибка вырезания и вставки из предыдущей версии примера, который у меня был. Я отредактирую это. - person Christian Gruber; 28.11.2013