Guice有多个混凝土......挑选其中一个

问题描述 投票:0回答:1

我注射了同一界面的多个混凝土。

我想出了指南“代码化”的惯例。

我的代码目前吐出来了

[INFO] App - About to ship. (abc)
[INFO] App - ShipperInterface . (FedExShipper)
[INFO] App - ShipperInterface . (UpsShipper)
[INFO] App - ShipperInterface . (UspsShipper)

所以我手指上有多个“托运人”。

注意方法:

public void ProcessOrder(String preferredShipperAbbreviation,Order ord){

我正在试图找出使用(String)preferredShipperAbbreviation选择3个具体托运人中的1个的最佳方法。

当我向Guice注册时,有没有办法“命名”我的3个混凝土?

或者选择三者之一的最佳方法是什么?

public class ProductionInjectModule extends AbstractModule implements Module {

  @Override
  protected void configure() {
    try {
      bind(OrderProcessorInterface.class).toConstructor(OrderProcessorImpl.class.getConstructor(Set.class));

      Multibinder<ShipperInterface> multibinder = Multibinder.newSetBinder(binder(), ShipperInterface.class);
      multibinder.addBinding().toConstructor(FedExShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      multibinder.addBinding().toConstructor(UpsShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      multibinder.addBinding().toConstructor(UspsShipper.class.getConstructor(org.apache.commons.logging.Log.class));

    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }

}

=============

import java.util.Collection;
import java.util.Set;

import org.apache.commons.logging.Log;


public class OrderProcessorImpl implements OrderProcessorInterface {

  private Log logger;
  Set<ShipperInterface> shippers;

  public OrderProcessorImpl(Log lgr, Set<ShipperInterface> shprs) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    if (null == shprs) {
      throw new IllegalArgumentException("ShipperInterface(s) is null");
    }

    this.logger = lgr;
    this.shippers = shprs;
  }

  public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
    this.logger.info(String.format("About to ship. (%1s)", preferredShipperAbbreviation));

    for (ShipperInterface sh : shippers) {
      this.logger.info(String.format("ShipperInterface . (%1s)", sh.getClass().getSimpleName()));
    }

  }
}

=============

public interface OrderProcessorInterface {

  void ProcessOrder(String preferredShipperAbbreviation, Order ord);

}

public class FedExShipper implements ShipperInterface {

  private Log logger;

  public FedExShipper(Log lgr) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    this.logger = lgr;
  }

  public void ShipOrder(Order ord) {
    this.logger.info("I'm shipping the Order with FexEx");
  }
}


public class UpsShipper implements ShipperInterface {

  private Log logger;

  public UpsShipper(Log lgr) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    this.logger = lgr;
  }

  public void ShipOrder(Order ord) {
    this.logger.info("I'm shipping the Order with Ups");
  }
}


public class UspsShipper implements ShipperInterface {

  private Log logger;

  public UspsShipper(Log lgr) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    this.logger = lgr;
  }

  public void ShipOrder(Order ord) {
    this.logger.info("I'm shipping the Order with Usps");
  }
}

..............

“主要”方法:

ProductionInjectModule pm = new ProductionInjectModule();
Injector injector = Guice.createInjector(pm);

Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder("WhatDoIPutHere?", ord);

===========下面的Guice版本:

    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.2.0</version>
    </dependency>

================================

我尝试这种方式的一种方式。这有什么好办法吗?

最终,在我的“真实”场景中(不是这个场景)......我希望将“concreteKey”保留为数据库/配置设置。

Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder(FedExShipper.class.getSimpleName(), ord);

  public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
    this.logger.info(String.format("About to ship. (%1s)", preferredShipperAbbreviation));

    ShipperInterface foundShipperInterface = this.FindShipperInterface(preferredShipperAbbreviation);
    foundShipperInterface.ShipOrder(ord);
  }

  private ShipperInterface FindShipperInterface(String preferredShipperAbbreviation) {

    /* requires java 8 */
    ShipperInterface foundShipperInterface = this.shippers
        .stream().filter(x -> x.getClass().getSimpleName().equalsIgnoreCase(preferredShipperAbbreviation)).findFirst().orElse(null);

    if(null == foundShipperInterface)
    {
      throw new NullPointerException(String.format("ShipperInterface not found in ShipperInterface collection. ('%1s')", preferredShipperAbbreviation));
    }

    return foundShipperInterface;
  }

============= APPEND ==================

由于Jeff B的回答/评论,我得到了这个工作。

import java.util.Map;
import java.util.Set;

import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;

public class ProductionInjectModule extends AbstractModule implements Module {

  @Override
  protected void configure() {
    try {

      MapBinder<String, ShipperInterface> mappyBinder = MapBinder.newMapBinder(binder(), String.class, ShipperInterface.class);
      mappyBinder.addBinding("myFedExName").toConstructor(FedExShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      mappyBinder.addBinding("myUPSName").toConstructor(UpsShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      mappyBinder.addBinding("myUSPSName").toConstructor(UspsShipper.class.getConstructor(org.apache.commons.logging.Log.class));

        /* below is not needed, but shows what needs to be injected */
      java.util.Map<String,  javax.inject.Provider<ShipperInterface>> shipperProviderMap;


    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }
}

================

import java.util.Collection;
import java.util.Set;

import org.apache.commons.logging.Log;

public class OrderProcessorImpl implements OrderProcessorInterface {

  private Log logger;
  private java.util.Map<String, javax.inject.Provider<ShipperInterface>> shipperProviderMap;

  public OrderProcessorImpl(Log lgr, java.util.Map<String, javax.inject.Provider<ShipperInterface>> spMap) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    if (null == spMap) {
      throw new IllegalArgumentException("Provider<ShipperInterface> is null");
    }

    this.logger = lgr;
    this.shipperProviderMap = spMap;
  }

  public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
    this.logger.info(String.format("About to ship. (%1s)", preferredShipperAbbreviation));


    ShipperInterface foundShipperInterface = this.FindShipperInterface(preferredShipperAbbreviation);
    foundShipperInterface.ShipOrder(ord);
  }

  private ShipperInterface FindShipperInterface(String preferredShipperAbbreviation) {

    ShipperInterface foundShipperInterface = this.shipperProviderMap.get(preferredShipperAbbreviation).get();

    if (null == foundShipperInterface) {
      throw new NullPointerException(
          String.format("ShipperInterface not found in shipperProviderMap. ('%1s')", preferredShipperAbbreviation));
    }

    return foundShipperInterface;
  }
}

================

“主要”方法

ProductionInjectModule pm = new ProductionInjectModule();
Injector injector = Guice.createInjector(pm);

Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder("myFedExName", ord); /* now use the "friendly named" strings */

OUTPUT:

[INFO] App - About to ship. (myFedExName)
[INFO] App - I'm shipping the Order with FexEx

我可能在我新发布的代码中有一些额外的“记录器”注入......但是简单的清理会让它运行起来。

guice
1个回答
2
投票

如果您使用Multibinder进行地图绑定,则可以使用MapBinder将每个Shipper实例绑定到Map中:

MapBinder<String, ShipperInterface> multibinder = MapBinder.newMapBinder(
    binder(), String.class, ShipperInterface.class);
multibinder.addBinding("FedEx").to(FedExShipper.class);
multibinder.addBinding("UPS").to(UpsShipper.class);
multibinder.addBinding("USPS").to(UspsShipper.class);

然后在你注入的课程中你可以注入一个Map<String, Provider<ShipperInterface>>

private ShipperInterface FindShipperInterface(String 
    preferredShipperAbbreviation) {

  ShipperInterface foundShipperInterface =
      providerMap.get(preferredShipperAbbreviation).get();
}

您也可以直接注入Map<String, ShipperInterface>,但Multibinder可以免费处理Provider间接,这样可以避免在实际只需要一个实例时创建三个ShipperInterface实例。此外,如果您的实例选择代码比根据您在编译时知道的一组实现中的字符串选择更复杂,那么you might still want a Factory implementation you write


作为旁注,ideally use @Inject annotations and bind(...).to(...) instead of toConstructor。这并不会将你绑定到Guice,因为@Inject是在JSR-330中定义的,并且您正在添加注释,您可以选择不在以后使用。您也可以在AbstractModule中编写一个@Provides方法,就像这样,它不比你的toConstructor绑定更脆弱:

@Provides UspsShipper provideUspsShipper(Log log) {
  return new UspsShipper(log);
}

当且仅当您使用遗留代码,您无法控制的代码,非常严格的代码样式规则或AOP(此处可能是这种情况)时,请使用toConstructor。为了一个简洁的例子,我上面已经这样做了,但如果有必要,你可以恢复到toConstructor

© www.soinside.com 2019 - 2024. All rights reserved.