【问题标题】:Spring doesn't create distinct beans when @Autowiring with different generic types当 @Autowiring 使用不同的泛型类型时,Spring 不会创建不同的 bean
【发布时间】:2020-06-15 00:14:54
【问题描述】:

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

我有这样的安排:

  • 两种具体类型:啤酒和咖啡
  • A店
  • 订单
  • @Autowire 订单 和订单 的 Manager 类
  • (然后每个订单@Autowires a Shop)
public class Beer {}
public class Coffee {}
@Component
public class Order<T> {

    @Autowired
    Shop<T> shopToSendOrderTo;

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

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

    @Autowired
    Order<Beer> beerOrder;

    @Autowired
    Order<Coffee> coffeeOrder;

    @Override
    public void afterPropertiesSet() {
        beerOrder.place(2);
        coffeeOrder.place(0);
    }
}

但是当我想要一个商店 和一个商店 以及一个订单 和一个订单 时,我最终只创建了一个 shop bean 和一个 order bean

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

"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"
   ]
}

当您打印此参考时,您会看到每个只有一个:

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

【问题讨论】:

  • 行为很复杂并且可能是一个bug,但是如果你想要单独的bean,最直接的方法是使用@Bean方法。
  • 您必须自己创建它们。 Bean 不是基于@Autowired 中的信息创建的,而是基于@Bean 信息或通过@Component 检测到的。它不会根据您想要的泛型类型自动创建实例(除非您开始为此目的扩展框架)。创建一个构造两个实例的配置类并删除@Component。
  • @M.Deinum 如果我接受你的建议并使用@Bean 注释为 CoffeeOrder 和 BeerOrder 创建一个 bean,在这些 bean 方法中我必须使用 new 实例化对象,所以 Shop每个内部都没有自动连接(?)你会如何处理?
  • 它仍然是一个 bean,spring 也会自动装配。 @Bean 与 xml 中的 &lt;bean&gt; 相同,基本上没有区别 bean 是如何存在的。除非您没有将 Order&lt;Coffee&gt; 等公开为 bean,而只是实例化它们。您必须确保 Shop&lt;Coffee&gt; 也可以作为 bean 使用。
  • 谢谢,我不知道@Bean 仍然会自动连线。我尝试为我的每个泛型创建具体类型:BeerShop 扩展 Shop 等,但又回到了使用 @Bean 的想法。自然地,我正在处理的代码库比这个示例更复杂,并且帮助很大。尽管 Spring 不会通过 @Beans 中具有泛型类型 的 @Autowired 属性进行连接,但它仍然更加整洁。谢谢。

标签: java spring spring-boot generics


【解决方案1】:

重要的是要知道,@Component 表示单例。这意味着,这种类型的 bean 只会被创建一次,并且在所有地方都将被注入同一个实例。

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

但有关泛型参数的信息仅在编译时可用。在运行时它不可用。这就是为什么在运行时Order&lt;Beer&gt; 和Order&lt;Coffee&gt; 的意思实际上是相同的,Order&lt;Object&gt;。这就是beerOrder 和coffeeOrder 具有相同值的原因。在执行器中,我们看到有一个 Order 类型的 bean,这是正确的。 Shop&lt;T&gt; 也是如此:无论您有多少引用,它们在运行时都意味着相同,Shop&lt;Object&gt;。

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

1) 使用原型作用域

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

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

然后在每个注入位置都会创建一个新实例。因此,beerOrder 和 coffeeOrder 将是不同的 bean 实例。相应地,Order 的每个实例都会有自己的Shop 实例。

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

2) 定义单独的类

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

因此,您可能希望定义一个具有Shop&lt;T&gt; 类型字段的抽象类。每个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);
  }

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    相关资源
    最近更新 更多