简体   繁体   English

Spring:没有限定符的 Autowire bean

[英]Spring: Autowire bean that does not have qualifier

Is it possible to autowire a bean that does NOT have the given qualifier in spring?是否可以在春季自动装配没有给定限定符的 bean? The use case would be to have a list of all beans, but exclude one:用例是拥有所有 bean 的列表,但排除一个:

@Autowired
@NotQualifier("excludedBean")    // <-- can we do something like this?
List<SomeBean> someBeanList;


public class Bean1 implements SomeBean {}

public class Bean2 implements SomeBean {}

@Qualifier("excludedBean")
public class Bean3 implements SomeBean {}

In the example above someList should contain an instance of Bean1 and Bean2 but not Bean3 .在上面的示例中, someList应该包含Bean1Bean2但不包含Bean3的实例。

(Remark: I'm aware that the opposite would work, ie add some qualifier to Bean1 and Bean2 and then autowire with that qualifier.) (备注:我知道反之亦然,即向Bean1Bean2添加一些限定符,然后使用该限定符自动装配。)

EDIT : Some further clarifications:编辑:一些进一步的澄清:

  • All beans are in the spring context (also the one being excluded).所有 bean 都在 spring 上下文中(也被排除在外)。
  • Configuration needs to be annotation-based, not xml-based.配置需要基于注解,而不是基于 xml。 Therefore, eg turning off autowired-candidate does not work .因此,例如关闭 autowired-candidate不起作用
  • Autowire capability of the bean must remain in general. bean 的自动装配能力必须保持一般。 In other words, I want to exclude the bean from the injection point List<SomeBean> someBeanList;换句话说,我想从注入点List<SomeBean> someBeanList; , but I want to autowire it somewhere else. ,但我想在其他地方自动接线。

You can introduce you own annotation with meta annotations @Conditional and @Qualifier您可以使用元注释@Conditional@Qualifier引入自己的注释

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@Conditional(MyCondition.class)
public @interface ExcludeBean {

and then introduce class where you can do your conditional logic然后介绍可以执行条件逻辑的类

public class MyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return !metadata.equals(ExcludeBean.class);
    }

}

In your Configuration class在您的配置类中

  @Bean
  @ExcludeBean
  public BeanA beanA() {
      return new BeanA();
  }

You can also exclude bean from being candidate for autowiring by setting autowire-candidate on particular bean or by specifying default-autowire-candidates="list of candidates here"您还可以通过在特定 bean 上设置autowire-candidate或通过default-autowire-candidates="list of candidates here"来将 bean 排除在自动装配候选者之外

main point it's bean for exclude is in context but not injected into some cases with exclude condition .要点它的排除 bean 是在上下文中,但没有注入到排除条件的某些情况下

you can do exclude bean with qualifier with custom annotaion and BeanPostProcessor.您可以使用自定义注释和 BeanPostProcessor 排除带有限定符的 bean。 (I did as example for simple case , for collection of bean type , but you can extend it) (我作为简单案例的示例,用于 bean 类型的集合,但您可以扩展它)

annotaion for exclude :排除注释:

@Component
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcludeBeanByQualifierForCollectionAutowired {

    String qualifierToExcludeValue();

    Class<?> aClass();
}

bean post processor with injection带有注入的 bean 后处理器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;

@Component
public class ExcludeAutowiredBeanPostProcessor implements BeanPostProcessor {

    @Autowired
    private ConfigurableListableBeanFactory configurableBeanFactory;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            ExcludeBeanByQualifierForCollectionAutowired myAutowiredExcludeAnnotation = field.getAnnotation(ExcludeBeanByQualifierForCollectionAutowired.class);
            if (myAutowiredExcludeAnnotation != null) {

                Collection<Object> beanForInjection = new ArrayList<>();

                String[] beanNamesOfType = configurableBeanFactory.getBeanNamesForType(myAutowiredExcludeAnnotation.aClass());
                for (String injectedCandidateBeanName : beanNamesOfType) {

                    Object beanCandidate = configurableBeanFactory.getBean(injectedCandidateBeanName);

                    Qualifier qualifierForBeanCandidate = beanCandidate.getClass().getDeclaredAnnotation(Qualifier.class);

                    if (qualifierForBeanCandidate == null || !qualifierForBeanCandidate.value().equals(myAutowiredExcludeAnnotation.qualifierToExcludeValue())) {
                        beanForInjection.add(beanCandidate);
                    }
                }
                try {
                    field.set(bean, beanForInjection);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

and example:和例子:

public class ParentBean {}

public class Bean1Included extends ParentBean {}

public class Bean2Included extends ParentBean {}

public class Bean3Included extends ParentBean {}

@Qualifier("excludedBean")
public class BeanExcluded extends ParentBean {}

configuration配置

@Configuration
public class BeanConfiguration {

    @Bean
    public Bean1Included getBean1(){
        return new Bean1Included();
    }

    @Bean
    public Bean2Included getBean2(){
        return new Bean2Included();
    }

    @Bean
    public Bean3Included getBean3(){
        return new Bean3Included();
    }

    @Bean
    public BeanExcluded getExcludedBean(){
        return new BeanExcluded();
    }

    @Bean
    public ExcludeAutowiredBeanPostProcessor excludeAutowiredBeanPostProcessor(){
        return new ExcludeAutowiredBeanPostProcessor();
    }
}

and result:结果:

@ExtendWith(SpringExtension.class) // assumes Junit 5
@ContextConfiguration(classes = BeanConfiguration.class)
public class ExcludeConditionTest {

    @Autowired
    private ApplicationContext context;
    @Autowired
    private BeanExcluded beanExcluded;
    @ExcludeBeanByQualifierForCollectionAutowired(qualifierToExcludeValue = "excludedBean" , aClass = ParentBean.class)
    private List<ParentBean> beensWithoutExclude;

    @Test
    void should_not_inject_excluded_bean() {
        assertThat(context.getBeansOfType(ParentBean.class).values())
                .hasOnlyElementsOfTypes(Bean1Included.class,
                                        Bean2Included.class,
                                        Bean3Included.class,
                                        BeanExcluded.class);

        assertThat(beansWithoutExclude)
                .hasOnlyElementsOfTypes(Bean1Included.class,
                                        Bean2Included.class,
                                        Bean3Included.class)
                .doesNotHaveAnyElementsOfTypes(BeanExcluded.class);

        assertThat(beanExcluded).isNotNull();
    }
}

There might be two cases :可能有两种情况:

case 1 : Bean3 in not in spring context;案例1:Bean3不在spring上下文中;

case 2 : Bean3 is in spring context but not injected in some cases with @Autowired ,案例 2:Bean3 在 spring 上下文中,但在某些情况下没有使用 @Autowired 注入,

  1. if you need to exclude bean with Qualifier from context at all ,use如果您需要从上下文中排除带有限定符的bean,请使用

    • Condition.健康)状况。 This bean is not registered in application conxtet if matches returns false.如果匹配返回 false,则此 bean 未在应用程序 conxtet 中注册。 as result :结果 :

    @Autowired List someBeanList; @Autowired List someBeanList; -- here injected all beans instanceof SomeBean and registered in application context. -- 这里注入了所有的bean instanceof SomeBean 并在应用程序上下文中注册。

    from spring api来自spring api

    Condition A single condition that must be matched in order for a component to be registered.条件 为了注册组件而必须匹配的单个条件。 Conditions are checked immediately before the bean-definition is due to be registered and are free to veto registration based on any criteria that can be determined at that point.在 bean 定义即将注册之前立即检查条件,并且可以根据当时可以确定的任何标准自由否决注册。

  2. autowired with qualifier :使用限定符自动装配:

    2.1 if you want to exclude bean with the some qualifier from autowired value in some bean/beans and in xml configuration you can use autowire-candidate 2.1 如果您想在某些 bean/beans 和 xml 配置中从 autowired 值中排除带有 some qualifier 的 bean,您可以使用 autowire-candidate

    2.2 also you can get all autowired values by Setter Injection and filter only beans that you need. 2.2 您还可以通过 Setter Injection 获取所有自动装配的值并仅过滤您需要的 bean。

     //no Autowired. Autowired in method private List<ParentBean> someBeen = new ArrayList<>(); @Autowired public void setSomeBeen(List<ParentBean> beens){ // if you use java 8 use stream api for (ParentBean bean:beens) { Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class); if(qualifier == null ||!qualifier.value().equals("excludedBean")){ someBeen.add(bean); } } }

    2.3 you can use custome AutowiredAnnotationBeanPostProcessor :) and customise @Autowired for you requirements if you need something realy custom. 2.3 如果您需要真正自定义的东西,您可以使用自定义AutowiredAnnotationBeanPostProcessor :) 并根据您的要求自定义 @Autowired。

from spring api AutowiredAnnotationBeanPostProcessor :来自 spring api AutowiredAnnotationBeanPostProcessor :

Note: A default AutowiredAnnotationBeanPostProcessor will be registered by the "context:annotation-config" and "context:component-scan" XML tags.注意:默认的 AutowiredAnnotationBeanPostProcessor 将由“context:annotation-config”和“context:component-scan”XML 标签注册。 Remove or turn off the default annotation configuration there if you intend to specify a custom AutowiredAnnotationBeanPostProcessor bean definition.如果您打算指定自定义 AutowiredAnnotationBeanPostProcessor bean 定义,请删除或关闭默认注释配置。

Another way to do this, is creating a custom component with @Qualifier另一种方法是使用@Qualifier创建自定义组件


@Component
@Qualifier
public @interface MyComponent {
    public boolean isMock() default false;
}

@Autowired
@MyComponent(true)
List<SomeBean> mockList; // will inject just component with "isMock = true"

@Autowired
@MyComponent(false)
List<SomeBean> notMockList; // will inject just component with "isMock = false"


@MyComponent
public class Bean1 implements SomeBean {}

@MyComponent
public class Bean2 implements SomeBean {}

@MyComponent(isMock = true)
public class Bean3 implements SomeBean {}

Obs: this code is not tested, just giving an idea Obs:此代码未经测试,只是提供一个想法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM