具有多种混凝土的 Spring DI(Beans)……选择其中一种 [英] Spring DI (Beans) with multiple concretes…picking one of them

查看:37
本文介绍了具有多种混凝土的 Spring DI(Beans)……选择其中一种的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我也有类似的问题

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

为 Guice 提供解决方案.

with a solution for Guice.

但是我有一个使用 spring di (beans) 的不同项目,但是有同样的问题.

But I have a different project using spring di (beans), but with the same kind of issue.

我有一个包含 N 个混凝土的接口.(这里 3 个)

I have an interface with N number of concretes. (3 here)

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");
  }
}

........

然后我有一堂课需要了解所有三个具体的内容.

Then I have a class that needs to know about ALL THREE concretes.

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;
  }
}

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

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

基本上,我想调用该方法,传入一个字符串参数,并让它为我选择具体的.(如果我的真实代码,这是通过数据库值,但对于演示代码,这已经足够了)

Basically, I want to call the method, pass in a string argument, and have it choose the concrete for me. (if my real code, this is via a database value, but for the demo code, this is good enough)

Order ord = new Order();
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
BeanFactory factory = context;

OrderProcessorInterface opi = context.getBean(OrderProcessorImpl.class);
opi.ProcessOrder("myFedExName", ord); /* friendlyName would be nice, but fully qualified concrete name also assceptable */

我的 Spring 配置是通过 xml:

My Spring Configuration is via xml:

 <bean id="theLoggerBean"
       class="org.apache.commons.logging.impl.Log4JLogger">
       <constructor-arg value="log" />
 </bean>    



<bean id="fedExBean"
    class="com.me.FedExShipper">
    <constructor-arg ref="theLoggerBean"></constructor-arg>
</bean>


<bean id="uspsExBean"
    class="com.me.FedExShipper">
    <constructor-arg ref="theLoggerBean"></constructor-arg>
</bean>


<bean id="upsExBean"
    class="com.me.FedExShipper">
    <constructor-arg ref="theLoggerBean"></constructor-arg>
</bean>

.........

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

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

<bean id="OrderProcessorImplBean"
    class="com.me.OrderProcessorImpl">

    <constructor-arg ref="theLoggerBean"></constructor-arg>

    <constructor-arg ref="How do I do N Number of ShipperInterfaces Here ??"></constructor-arg>

</bean>

所以我想 xml 配置 3 个混凝土.

So I want to xml configure the 3 concretes.

然后将它们注入到类中.

And then inject them into the class.

但是我有How do I do N Number of ShipperInterfaces Here ??",我不知道该怎么做.

But where I have "How do I do N Number of ShipperInterfaces Here ??", I have no idea what to do.

首选 JSR 330 实现,但会采取任何措施.

JSR 330 implementation preferred, but will take anything.

谢谢

注意,在另一个问题(Guice 问题)中,这也是 OrderProcessor 的构造函数的一种可能性:

Note, in the other question (the Guice one), this was also a possiblity for the constructor of the OrderProcessor:

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()));
    }

  }
}

推荐答案

这样的事情应该可行.这使用@Autowired 而不是 xml 配置:

Something like this should work. This uses @Autowired and not xml configuration:

@org.springframework.stereotype.Service
public class OrderProcessorImpl implements OrderProcessorInterface {

    private List<ShipperInterface> shipperProviders;

    private Map<String, ShipperInterface> shipperProvidersMap = new HashMap<>();

    @Autowired
    public void setShipperProviders(List<ShipperInterface> shipperProviders) {
        this.shipperProviders= shipperProviders;

        this.shipperProviders.stream().forEach(p->shipperProvidersMap .put(/* your code for getting the key */, p));
    }

Gradle 依赖提示:

Gradle dependency hint:

compile group: 'org.springframework', name: 'spring-context', version: '5.1.9.RELEASE'

这篇关于具有多种混凝土的 Spring DI(Beans)……选择其中一种的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆