Как использовать фабрики Guice's AssistedInject вместе с сервисным загрузчиком?

java dependency-injection guice guice-3

314 просмотра

1 ответ

863 Репутация автора

В моем модуле guice у меня есть несколько фабрик, как показано ниже:

install(new FactoryModuleBuilder().implement(SportsCar.class,Ferrari.class).build(FerrariFactory.class));
install(new FactoryModuleBuilder().implement(LuxuryCar.class,Mercedes.class).build(MercedesFactory.class));

Обе фабрики имеют следующий метод создания, который принимает вспомогательный элемент:

Ferrari create(@Assisted Element partsElement);

Mercedescreate(@Assisted Element partsElement);

В классе CarChooser я получаю экземпляры Ferrari или Mercedes, как показано ниже:

@Inject 
public CarChooser(FerrariFactory ferrariFactory , MercedesFactory mercedesFactory )
{
        this.ferrariFactory = ferrariFactory;
        this.mercedesFactory = mercedesFactory;
} 

В том же классе:

if(type.equals("ferrari"))
    ferrariFactory.create(partsElement);
else if (type.equals("mercedes"))
    mercedesFactory.create(partsElement);
.....
......

Теперь я пытаюсь сделать этот класс CarChooser открытым для расширения, но закрытым для модификации. т.е. если мне нужно добавить еще одну фабрику, мне не нужно объявлять ее как переменную + добавить ее в конструктор + добавить еще одно условие if для соответствующего нового типа. Я планировал использовать ServiceLoader здесь и объявить интерфейс CarFactory, который будет реализован всеми фабриками (такими как FerrariFactory, MercedesFactory и т. Д.), И все реализации будут иметь метод getCarType. Но как я могу вызвать метод create с помощью Service Loader?

ServiceLoader<CarFactory> impl = ServiceLoader.load(CarFactory.class);

        for (CarFactory fac: impl) {
            if(type.equals(fac.getCarType()))
               fac.create(partsElement);
        }

Это правильный путь, если он работает (я даже не уверен, что это будет работать). Или есть лучший способ сделать то же самое?

Благодаря первому комментарию к посту я знаю, что хочу использовать MapBinder. Я написал CarFactory, который расширяется как FerrariFactory, так и MercedesFactory. Поэтому я добавляю следующее:

    MapBinder<String, CarFactory> mapbinder = MapBinder.newMapBinder(binder(), String.class, CarFactory.class);

   mapbinder.addBinding("Ferrari").to(FerrariFactory.class);
   mapbinder.addBinding("Mercedes").to(MercedesFactory.class);

Но так как часть .to вышеприведенного кода является абстрактным классом, я получаю ошибку инициализации, что FerrariFactory не связана ни с какой реализацией. Что я должен иметь здесь, чтобы привязать его к правильной фабрике вспомогательных инъекций, объявленной с FactoryModuleBuilder?

Автор: Crusaderpyro Источник Размещён: 18.07.2016 06:44

Ответы (1)


0 плюса

863 Репутация автора

Решение

Таким образом, использование MapBinder вместе с генериками является решением.

    install(new FactoryModuleBuilder().implement(SportsCar.class,Ferrari.class).build(FerrariFactory.class));
    install(new FactoryModuleBuilder().implement(LuxuryCar.class,Mercedes.class).build(MercedesFactory.class));



    MapBinder<String, CarFactory<?>> mapbinder = MapBinder.newMapBinder(binder(), new TypeLiteral<String>(){}, new TypeLiteral<CarFactory<?>>(){});

     mapbinder.addBinding("ferrari").to(FerrariFactory.class);  
     mapbinder.addBinding("mercedes").to(MercedesFactory.class);

Важно отметить, что это поддерживается только в Guice 3.0 + JDK 7. Для JDK 8 вам нужен Guice 4.0! Нашел эту проблему на https://github.com/google/guice/issues/904

Надеюсь, это поможет.

Подробнее о решении:

http://crusaderpyro.blogspot.sg/2016/07/google-guice-how-to-use-mapbinder.html

Автор: Crusaderpyro Размещён: 30.07.2016 10:34
Вопросы из категории :
32x32