Dependency injection 如果存在另一个相同类型的CDIBean,则否决该CDIBean

Dependency injection 如果存在另一个相同类型的CDIBean,则否决该CDIBean,dependency-injection,cdi,Dependency Injection,Cdi,我不确定我的想法是否正确,我正在[CDI]中寻找类似于Spring中的东西-@ConditionalOnMissingBean-这允许您告诉Spring-仅当指定的bean丢失时才创建 我尝试过使用扩展,看起来可以点击几个事件,并使用这些事件否决bean。一种方法可能是在这个阶段使用BeanManager,查找已经存在的bean,如果它包含您将要注入的bean,则否决这个bean。但是,这只有在我们查看了所有的bean之后才会起作用 然而,在beandiscovery看起来合适之后,在它被调用之

我不确定我的想法是否正确,我正在[CDI]中寻找类似于Spring中的东西-@ConditionalOnMissingBean-这允许您告诉Spring-仅当指定的bean丢失时才创建

我尝试过使用扩展,看起来可以点击几个事件,并使用这些事件否决bean。一种方法可能是在这个阶段使用BeanManager,查找已经存在的bean,如果它包含您将要注入的bean,则否决这个bean。但是,这只有在我们查看了所有的bean之后才会起作用

然而,在beandiscovery看起来合适之后,在它被调用之前,验证失败,抱怨有多个相同类型的bean


如果我能在这里得到帮助就太好了

您的问题很有趣,可以使用CDI扩展来解决(实际上几乎与您描述的一样),请参见下面的简单、有效的概念验证实现。它是幼稚的,因为它不处理例如生产者方法/字段,并且可能缺少更多

CDI扩展确实很棒,功能强大,但是技术性很强,所以让我们先讨论其他替代方案

  • 专门化:也许您的用例可以明确地记录您通过(比如,
    公共类SomeServiceDefaultImpl
    提供
    SomeService
    的默认实现,并且为了覆盖它,开发人员应该:

    @Specializes
    public class SomeServiceSpecialImpl extends SomeServiceDefaultImpl {...}
    

    也考虑备选方案,如John Ament评论中提到的。

  • 限定符:如果此服务仅在一个地方/几个地方使用,并且仅在代码中使用,您可以使用自定义限定符限定
    SomeServiceDefaultImpl
    ,例如
    @MyDefaultImpl
    。然后注入一个
    实例
    ,首先查找一个不合格的实例,如果不合格,则查找合格的实例,如下所示:

    private SomeService someService;
    
    @Inject
    void setSomeServiceInstance(Instance<SomeService> s) {
        // not tried, please adapt as needed
        if( s.isUnsatisfied() ) {
            someService = s.select(new MyDefaultImplAnnotation()).get();
        }
        else {
            someService = s.get();
        }
    }
    
    value()
    是必需的,表示“默认”的bean类型。您的实现可以更智能,即从实际的默认实现中检测bean类型,但是,嘿,这只是一个概念证明

  • 公然无视制片人

  • 很容易测试,所以可能有邪恶的角落案例,所以要小心

  • 除了代码之外,还需要扩展的所有编排(META-INF/services/javax.enterprise.inject.spi.extension,beans.xml)


    我相信您正在寻找的是我们在CDI中所称的
    替代方案
    。但我想更好地理解。您有一个接口
    Interface1
    。当且仅当没有其他实现注册为bean时,您才希望安装此接口的实现,对吗?是的,我不希望此上下文中有多个相同类型的bean,因为这会导致“不明确的依赖关系”之类的事情。我只想在上下文中没有其他同类bean的情况下生成一个。如果有,制作人不应该插手。感谢Nikos,我想我也尝试过这种类型的东西,但没有否决带有“条件”注释的,因此,在我到达后Beandiscovery阶段之前就出现了验证错误。我犯了一个错误,非常感谢,Niko。专门化和限定符可以工作,但是,这并不完全是我想要的——如果已经存在相同类型的专门化bean,我不希望上下文中出现专门化bean。有了@Specialization,我将用我的来覆盖现有的-不适用于这种情况-我只希望我的存在存在于没有任何其他(超出我的控制范围-我的是一个库)的情况下。
    @Target({ TYPE, METHOD, FIELD })
    @Retention(RUNTIME)
    @Documented
    public @interface ConditionalOnMissingBean {
        Class<?> value();
    }
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.enterprise.event.Observes;
    import javax.enterprise.inject.spi.AfterBeanDiscovery;
    import javax.enterprise.inject.spi.AnnotatedType;
    import javax.enterprise.inject.spi.Bean;
    import javax.enterprise.inject.spi.BeanAttributes;
    import javax.enterprise.inject.spi.BeanManager;
    import javax.enterprise.inject.spi.Extension;
    import javax.enterprise.inject.spi.InjectionTargetFactory;
    import javax.enterprise.inject.spi.ProcessAnnotatedType;
    
    public class ConditionalOnMissingBeanExtension implements Extension {
    
        private Map<Class<?>, AnnotatedType<?>> map = new HashMap<>();
    
        <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
            AnnotatedType<?> annotatedType = pat.getAnnotatedType();
            ConditionalOnMissingBean annotation = annotatedType.getAnnotation(ConditionalOnMissingBean.class);
            if( annotation != null ) {
                map.put(annotation.value(), annotatedType);
                pat.veto();
            }
        }
    
        void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager beanManager) {
            map.entrySet().stream()
                .filter(e -> doesNotHaveBeanOfType(beanManager, e.getKey()))
                .map(e -> defineBean(beanManager, e.getValue()))
                .forEach(abd::addBean);
            map = null;
        }
    
        private boolean doesNotHaveBeanOfType(BeanManager beanManager, Class<?> type) {
            return beanManager.getBeans(type).isEmpty();
        }
    
        private <T> Bean<T> defineBean(BeanManager beanManager, AnnotatedType<T> annotatedType) {
            BeanAttributes<T> beanAttributes = beanManager.createBeanAttributes(annotatedType);
            InjectionTargetFactory<T> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType);
            return beanManager.createBean(beanAttributes, annotatedType.getJavaClass(), injectionTargetFactory);
        }
    }
    
    @ApplicationScoped
    @ConditionalOnMissingBean(SomeService.class)
    public class SomeServiceDefaultImpl implements SomeService {
    
        @Override
        public String doSomeCalculation() {
            return "from default implementation";
        }
    }