Java Spring Boot@ConditionalOnMissingBean和泛型类型

Java Spring Boot@ConditionalOnMissingBean和泛型类型,java,spring,generics,Java,Spring,Generics,是否有任何方法可以实例化这两个bean: @Bean @ConditionalOnMissingBean public Container<Book> bookContainer() { return new Container<>(new Book()); } @Bean @ConditionalOnMissingBean public Container<Computer> computerContainer() { return new

是否有任何方法可以实例化这两个bean:

@Bean
@ConditionalOnMissingBean
public Container<Book> bookContainer() {
    return new Container<>(new Book());
}

@Bean
@ConditionalOnMissingBean
public Container<Computer> computerContainer() {
    return new Container<>(new Computer());
}
@Bean
@条件性缺豆
公共容器bookContainer(){
归还新容器(新书());
}
@豆子
@条件性缺豆
公共容器计算机容器(){
返回新容器(新计算机());
}
@ConditionalOnMissingBean
只考虑bean类,错误地将
bookContainer
computerContainer
设置为相同的类型,因此只注册其中一个

我可以为每一个都提供明确的限定符,但由于这是Spring启动程序的一部分,使用起来会很烦人,因为用户将被迫知道要覆盖的确切名称,而不仅仅是类型

潜在方法:

因为可以通过完整的泛型类型向Spring请求bean,所以我可能能够实现一个条件工厂,该工厂将尝试获取一个完整类型的实例,并在它不存在的情况下生成一个实例。我现在正在调查是否/如何做到这一点。 令人惊讶的是,当实现自定义条件(与
@Conditional
一起使用)时,它没有访问bean类型的权限

虽然所有其他现代注入框架都是在完整类型上运行的,但Spring仍然以某种方式处理原始类和字符串名称(!),这让我非常震惊。。。有什么解决办法吗

有什么解决办法吗


不幸的是,我不这么认为。问题是Java中的泛型不存在于编译的字节码中——它们只用于编译时的类型检查,并且只存在于源代码中。因此,在运行时,当SpringBoot看到您的bean配置类时,它只看到两个bean定义,它们都创建了一个容器。它看不出它们之间有任何区别,除非您自己提供标识符。

更新:Spring现在以本机方式支持此功能。见公认的答案

这是一个完全有效的解决方案。我放弃了这种方法,但我正在发布它,以防有人发现它有用

我做了一个自定义注释:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(MissingGenericBeanCondition.class)
public @interface ConditionalOnMissingGenericBean {

    Class<?> containerType() default Void.class;
    Class<?>[] typeParameters() default {};
}
@ConditionalOnMissingGenericBean(declaringClass=CustomConfig.class)
然后用它来代替

Class<?> declaringClass = ClassUtils.forName(methodMeta.getDeclaringClassName(), context.getClassLoader());
Class declaringClass=ClassUtils.forName(methodMeta.getDeclaringClassName(),context.getClassLoader());

自Spring Boot v2.1.0以来,这是可能的

该版本在
ConditionalOnMissingBean
上引入了新字段
parameterizedContainer

@Bean
@ConditionalOnMissingBean(value = Book.class, parameterizedContainer = Container.class)
public Container<Book> bookContainer() {
    return new Container<>(new Book());
}

@Bean
@ConditionalOnMissingBean(value = Computer.class, parameterizedContainer = Container.class)
public Container<Computer> computerContainer() {
    return new Container<>(new Computer());
}
@Bean
@ConditionalOnMissingBean(值=Book.class,参数化容器=Container.class)
公共容器bookContainer(){
归还新容器(新书());
}
@豆子
@ConditionalOnMissingBean(值=Computer.class,parameterizedContainer=Container.class)
公共容器计算机容器(){
返回新容器(新计算机());
}

您是否尝试设置其名称parameter@neo你说得对,我可以。但这是Spring启动程序的一部分,因此显式名称会使使用变得很烦人,因为用户将被迫知道要覆盖的确切名称,而不仅仅是类型。使用此信息更新了问题。在实现自定义条件时,类
@AnnotatedTypeMetadata
的底层实现实际上是
@StandardMethodMetadata
。它有一个名为
#GetInspectedMethod
的公共方法,然后你可以得到相应方法的参考这不是真的。。。虽然实例上的泛型信息丢失了,但它仍然存在于方法的返回类型、参数类型等中。有关我的意思的示例,请参见
method\getGenericReturnType
。因此,Spring确实可以通过查看生成bean的方法的完整泛型类型来访问bean的完整类型。CDI和其他框架也利用了这一点。它通常在春季也能正常工作,在这种情况下,问题在于
@ConditionalOnMissingBean
。请参阅我提出的解决方法…使用2.2.11进行测试不起作用。然后您犯了一个错误。也许看一下API可以帮助您:
@Bean
@ConditionalOnMissingGenericBean(containerType=Container.class, typeParameters=Computer.class)
public Container<Computer> computerContainer() {
    return new Container<>(new Computer());
}
@ConditionalOnMissingGenericBean(declaringClass=CustomConfig.class)
Class<?> declaringClass = ClassUtils.forName(methodMeta.getDeclaringClassName(), context.getClassLoader());
@Bean
@ConditionalOnMissingBean(value = Book.class, parameterizedContainer = Container.class)
public Container<Book> bookContainer() {
    return new Container<>(new Book());
}

@Bean
@ConditionalOnMissingBean(value = Computer.class, parameterizedContainer = Container.class)
public Container<Computer> computerContainer() {
    return new Container<>(new Computer());
}