当 @Autowiring 具有不同的泛型类型时,Spring 不会创建不同的 bean

spl*_*spl 5 java generics spring spring-boot

我试图找出在 Spring 5 中使用组件的多个副本(通过泛型类型进行区分)的最简洁、最有效的方法。显然我错过了一个概念,因为根据我读到的内容,我觉得这个(下面)应该起作用,但它不起作用,所以我可能误解了泛型类型如何作为限定符工作。

我有这样的安排:

  • 两种具体类型:啤酒和咖啡
  • 一家商店<T>
  • 订单<T>
  • 一个 Manager 类 @Autowire 一个 Order < Beer > 和一个 Order < Coffee >
  • (然后每个订单<T>@Autowires一个Shop<T>)
public class Beer {}
Run Code Online (Sandbox Code Playgroud)
public class Coffee {}
Run Code Online (Sandbox Code Playgroud)
@Component
public class Order<T> {

    @Autowired
    Shop<T> shopToSendOrderTo;

    void place(int quantity) {
        log.info("Inside the order: " + this);
        shopToSendOrderTo.sendOrder(quantity);
    }
}
Run Code Online (Sandbox Code Playgroud)
@Component
public class Shop<T> {

    void sendOrder(int quantity) {
        log.info("Inside the shop: " + this);
    }
}
Run Code Online (Sandbox Code Playgroud)
@Configuration
public class Manager implements InitializingBean {

    @Autowired
    Order<Beer> beerOrder;

    @Autowired
    Order<Coffee> coffeeOrder;

    @Override
    public void afterPropertiesSet() {
        beerOrder.place(2);
        coffeeOrder.place(0);
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,当我想要一家商店<啤酒>和一家商店<咖啡>以及一份订单<啤酒>和一份订单<咖啡>时,我最终只创建了一个商店bean和一个订单bean

我可以从执行器/beans 输出中清楚地看到这一点:

"shop":{
   "aliases":[
   ],
   "scope":"singleton",
   "type":"com.example.generics.Generics.Shop",
   "resource":"file [...]",
   "dependencies":[

   ]
},
"order":{
   "aliases":[
   ],
   "scope":"singleton",
   "type":"com.example.generics.Generics.Order",
   "resource":"...",
   "dependencies":[
      "shop"
   ]
}
Run Code Online (Sandbox Code Playgroud)

当您打印此引用时,您可以看到每一个都只有一个:

2020-06-15 00:40:05.600  INFO 849 --- [  restartedMain] c.e.g.Generics.Order : Inside the order: com.example.generics.Generics.Order@70792f0a
2020-06-15 00:40:05.602  INFO 849 --- [  restartedMain] c.e.g.Generics.Shop  : Inside the shop: com.example.generics.Generics.Shop@41e7f2d5
2020-06-15 00:40:05.602  INFO 849 --- [  restartedMain] c.e.g.Generics.Order : Inside the order: com.example.generics.Generics.Order@70792f0a
2020-06-15 00:40:05.602  INFO 849 --- [  restartedMain] c.e.g.Generics.Shop  : Inside the shop: com.example.generics.Generics.Shop@41e7f2d5
Run Code Online (Sandbox Code Playgroud)

men*_*urg 4

重要的是要知道,这@Component意味着单身人士。这意味着,这种类型的 bean 将仅创建一次,并且在所有位置都将注入相同的实例。

如果这些类没有使用泛型,那么很容易理解您所看到的内容。

但有关泛型参数的信息仅在编译时可用。在运行时它不可用。这就是为什么在运行时Order<Beer>和 的Order<Coffee>含义实际上是相同的Order<Object>。这就是为什么beerOrdercoffeeOrder具有相同的值。在执行器中,我们看到有一个类型为 的 bean Order,这是正确的。这同样适用于Shop<T>:无论您有多少个引用,它们在运行时的含义都是相同的Shop<Object>

不同的解决方案可以得到不同的豆子。

1)使用原型范围

@Component
@Scope("prototype")
public class Order<T> {
  ...

@Component
@Scope("prototype")
public class Shop<T> {
  ...

Run Code Online (Sandbox Code Playgroud)

然后在每个注入位置都会创建一个新实例。结果,beerOrdercoffeeOrder将是不同的 bean 实例。相应地,每个 的实例Order将有其自己的 的实例Shop

但是:如果您在代码中的某个位置有其他类型的注入 bean Order<Beer>,则它们中的每个都将是一个单独的实例。你决定,如果这是你想要的。

2)定义单独的类

如果您希望Order<Beer>在代码中的所有位置注入相同的 bean 实例(单例),并且Order<Coffee>在所有位置注入相同的 bean 实例(另一个单例),但与 的位置不同Order<Coffee>,那么可能的解决方案是显式定义相应的类。

因此,您可能想要定义一个具有类型字段的抽象类Shop<T>。每个Order类都必须Shop向父构造函数提供自己的副本。代码可能如下所示:

public abstract class AbstractOrder extends Order<T> {
  private Shop<T> shop;

  protected AbstractOrder(Shop<T> shop) {
    this.shop = shop;
  }

@Component
public class BeerOrder extends Order<Beer> {
  ...
  public BeerOrder(BeerShop shop) {
    super(shop);
  }

@Component
public class CoffeeOrder extends Order<Coffee> {
  ...
  public CoffeeOrder(CoffeeShop shop) {
    super(shop);
  }

Run Code Online (Sandbox Code Playgroud)

在这种情况下,Spring 将为每种类型创建一个 bean。在需要的地方,Spring 会将适当的 bean 传递给构造函数。