Spring 框架 - 注入 bean 的子集

Spring Framework - Inject subset of beans

我正在尝试控制 spring 中的依赖项注入,以便 @Autowired 注释只注入一部分 bean,但我还没有遇到提供此功能的解决方案行为。

想象一个场景,有很多特定类型的 bean,但您只想在各种上下文中使用某些 bean。 @Autowired 注释支持 bean 集合,但它似乎不支持对可以注入的各种 bean 应用限制(AFAIK)。

@Autowired
Set<Foo> beansForContextA;

@Autowired
Set<Foo> beansForContextB;

以上代码会将同一组 bean 注入到 beansForContextAbeansForContextB 中。我想要一种方法来区分哪些 bean 被注入到它各自的 属性 中。 spring 中是否内置了支持此用例的内容?

考虑以下伪代码来更清楚地说明期望:

@Configuration
public class Config {
    @Bean(tags = {"A"})
    Foo bean1() {
        ...
    }

    @Bean(tags = {"B"})
    Foo bean2() {
        ...
    }

    @Bean(tags = {"A", "B"})
    Foo bean3() {
        ...
    }
}

@Configuration
public class Impl {
    @Autowired(tags = {"A"})
    Set<Foo> beansForContextA;

    @Autowired(tags = {"B"})
    Set<Foo> beansForContextB;

    public void execute() {
        for (Foo foo : someCondition ? beansForContextA : beansForContextB) {
            ...
        }
    }
}

这里我希望 beansForContextAbean1bean3beansForContextBbean2bean3。这可以通过扩展 @Autowired@Bean 注释来支持 "tags" 来实现,但我很好奇是否已经有解决方案,或者我是否必须发明自己的解决方案?

或者,这是个好主意吗?也许还有另一种方法我还没有遇到过。

另一个简单的解决方案是使用 spring 的新 annotation.collect beans 并通过新注释删除不需要的 beans。

public @interface BeanTag {
String value();
}

使用方法

@Bean
Foo bean1TypeA() {
    Foo foo = new Foo();
    foo.setType("typeA");
    return new Foo();
}
@Bean
Foo bean2TypeA() {
    Foo foo = new Foo();
    foo.setType("typeA");
    return new Foo();
}

@Bean
Foo bean1TypeB() {
    Foo foo = new Foo();
    foo.setType("typeB");
    return new Foo();
}

@Bean
Foo bean2TypeB() {
    Foo foo = new Foo();
    foo.setType("typeB");
    return new Foo();
}

@Autowired
@BeanTag("typeA")
private Set<Foo> collectionTypeA;

@Autowired
@BeanTag("typeB")
private Set<Foo> collectionTypeB;


@PostConstruct
public void removeNotNeedType(){
    // find all the field with @BeanTag remove not need bean

}

@Qualifier注解可以实现这一点。开箱即用,您可以使用字符串来限定 bean 和注入点:

@Autowired
@Qualifier("a")
Set<Foo> beansForContextA;

@Autowired
@Qualifier("b")
Set<Foo> beansForContextB;


@Configuration
public class Config {
    @Bean
    @Qualifier("a")
    Foo bean1() {
        ...
    }

    @Bean
    @Qualifier("b")
    Foo bean2() {
        ...
    }
}

然而,这对您的情况不起作用,因为 @Qualifier 的多个实例不能出现在同一个地方。 You have to create custom annotations which are annotated with Qualifier and use those instead of @Qualifier and a string name.

@Target(value={FIELD,METHOD,PARAMETER,TYPE,ANNOTATION_TYPE})
 @Retention(value=RUNTIME)
 @Inherited
 @Qualifier
public interface AScope

@Target(value={FIELD,METHOD,PARAMETER,TYPE,ANNOTATION_TYPE})
 @Retention(value=RUNTIME)
 @Inherited
 @Qualifier
public interface BScope

使用这些自定义注释,您最终会得到这样的结果。

@Configuration
public class Config {
    @Bean
    @AScope
    Foo bean1() {
        ...
    }

    @Bean
    @BScope
    Foo bean2() {
        ...
    }

    @Bean
    @AScope
    @BScope
    Foo bean3() {
        ...
    }
}

在注入点:

@Configuration
public class Impl {
    @Autowired
    @AScope
    Set<Foo> beansForContextA;

    @Autowired
    @BScope
    Set<Foo> beansForContextB;

    public void execute() {
        for (Foo foo : someCondition ? beansForContextA : beansForContextB) {
            ...
        }
    }
}