簡體   English   中英

Spring:沒有限定符的 Autowire bean

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

是否可以在春季自動裝配沒有給定限定符的 bean? 用例是擁有所有 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 {}

在上面的示例中, someList應該包含Bean1Bean2但不包含Bean3的實例。

(備注:我知道反之亦然,即向Bean1Bean2添加一些限定符,然后使用該限定符自動裝配。)

編輯:一些進一步的澄清:

  • 所有 bean 都在 spring 上下文中(也被排除在外)。
  • 配置需要基於注解,而不是基於 xml。 因此,例如關閉 autowired-candidate不起作用
  • bean 的自動裝配能力必須保持一般。 換句話說,我想從注入點List<SomeBean> someBeanList; ,但我想在其他地方自動接線。

您可以使用元注釋@Conditional@Qualifier引入自己的注釋

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

然后介紹可以執行條件邏輯的類

public class MyCondition implements Condition {

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

}

在您的配置類中

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

您還可以通過在特定 bean 上設置autowire-candidate或通過default-autowire-candidates="list of candidates here"來將 bean 排除在自動裝配候選者之外

要點它的排除 bean 是在上下文中,但沒有注入到排除條件的某些情況下

您可以使用自定義注釋和 BeanPostProcessor 排除帶有限定符的 bean。 (我作為簡單案例的示例,用於 bean 類型的集合,但您可以擴展它)

排除注釋:

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

    String qualifierToExcludeValue();

    Class<?> aClass();
}

帶有注入的 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;
    }
}

和例子:

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
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();
    }
}

結果:

@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();
    }
}

可能有兩種情況:

案例1:Bean3不在spring上下文中;

案例 2:Bean3 在 spring 上下文中,但在某些情況下沒有使用 @Autowired 注入,

  1. 如果您需要從上下文中排除帶有限定符的bean,請使用

    • 健康)狀況。 如果匹配返回 false,則此 bean 未在應用程序 conxtet 中注冊。 結果 :

    @Autowired List someBeanList; -- 這里注入了所有的bean instanceof SomeBean 並在應用程序上下文中注冊。

    來自spring api

    條件 為了注冊組件而必須匹配的單個條件。 在 bean 定義即將注冊之前立即檢查條件,並且可以根據當時可以確定的任何標准自由否決注冊。

  2. 使用限定符自動裝配:

    2.1 如果您想在某些 bean/beans 和 xml 配置中從 autowired 值中排除帶有 some qualifier 的 bean,您可以使用 autowire-candidate

    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 如果您需要真正自定義的東西,您可以使用自定義AutowiredAnnotationBeanPostProcessor :) 並根據您的要求自定義 @Autowired。

來自 spring api AutowiredAnnotationBeanPostProcessor :

注意:默認的 AutowiredAnnotationBeanPostProcessor 將由“context:annotation-config”和“context:component-scan”XML 標簽注冊。 如果您打算指定自定義 AutowiredAnnotationBeanPostProcessor bean 定義,請刪除或關閉默認注釋配置。

另一種方法是使用@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:此代碼未經測試,只是提供一個想法

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM