2012-07-02 2 views
4

Я пытаюсь выяснить, лучшие практики для решения следующей ситуации:Иерархические зависимости с Guice

public class AppModule extends Module { 

    @Override 
    protected void configure() { 
    install(new JpaPersistModule("myJpaUnit").addFinder(Dao.class)); 
    bind(MyJpaInitializer.class).asEagerSingleton(); 
    } 

    @Provides 
    @IndicatesSomeConstantMap 
    @Singleton 
    Map<String, String> getMappings(Dao dao) { 
    ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>(); 
    // Build map from Dao 
    return builder.build(); 
    } 

} 

Мне нужно вводить @IndicatesSomeConstantMap в других классах. Кажется, единственный способ, с помощью которого getMappings может получить Дао, - это привязать MyJpaInitializer как EagerSingleton, который чувствует себя не так. Каков предпочтительный способ решения этих иерархических зависимостей?

EDIT:

На основании ответа от @jeffcrowe я придумал что-то вроде:

public class Module1 extends PrivateModule { 

    @BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) 
    public @interface Jpa1{} 

    @Singleton 
    public static class JpaInitializer1 { 
    @Inject 
    public JpaInitializer1(@Jpa1 PersistService service) { 
     service.start(); 
    } 
    } 

    public interface Finder1 { 
    @Finder(query="FROM Foo", returnAs = ArrayList.class) 
    List<Foo> getAll(); 
    } 

    @Override 
    protected void configure() { 
    install(new JpaPersistModule("firstJpaUnit").addFinder(Finder1.class)); 
    bind(JpaInitializer1.class); 
    } 

    @Provides 
    @Exposed 
    @Jpa1 
    PersistService getPersistService(Provider<PersistService> provider) { 
    return provider.get(); 
    } 

    @Provides 
    @Exposed 
    @Jpa1 
    Finder1 getFinder(Finder1 finder, JpaInitializer1 init) { 
    return finder; 
    } 

} 

Это обрабатывает зависимость, окружив его за поставщика и чувствует себя чище меня, чем при использовании eagerSingleton. Это также скрывает JpaModule за частным модулем, что делает его полезным в ситуации, когда связаны несколько модулей сохранения. Новая проблема заключается в том, что поскольку Finder уже связан с JpaPersistModule, мы должны добавить аннотацию @ Jpa1 к каждой инъекции Finder1. Есть ли способ обойти это?

ответ

2

Это интересный случай. Обычно в подобном сценарии вы можете связать инициализатор в обычной области Singleton и вставить его в реализацию Dao, и это обеспечит, чтобы это было сделано до того, как Dao был использован. Из-за того, как настроены модули персистентности Jpa, не существует простого способа добавить эту зависимость.

Как указывал OP, JpaPersistModule является окончательным, поэтому мы не можем обойти это, подклассифицируя его. Мы можем, однако, обернуть связующее, используемое для установки JpaPersistModule.

Сначала оберните связующее в прокси с переопределенным методом bind() для перехвата привязки EntityManager.class. (BinderProxy реализует Binder и передает каждый вызов подшивки, указанный в его конструкторе. Source available here)

new BinderProxy(binder()) { 
    @Override 
    public <T> AnnotatedBindingBuilder<T> bind(Class<T> clazz) { 
    if (clazz == EntityManager.class) { 
     return (AnnotatedBindingBuilder<T>) super.bind(clazz).annotatedWith(DefaultEntityManager.class); 
    } else { 
     return super.bind(clazz); 
    } 
    } 
}.install(new JpaPersistModule("myJpaUnit")); 

Затем добавить предоставляет метод к модулю, который обеспечивает JPA инициализацию до того, как EntityManager используется

@Provides EntityManager provideEm(MyJpaInitializer init, @DefaultEntityManager EntityManager em){ 
    return em; 
} 
+0

Кроме того, только заметил ответ, добавленный к вопросу выше. Выглядит неплохо и не нуждается ни в одном из моих обертываний! – idle

Смежные вопросы