我不确定我是否正确思考这个问题,我在[CDI]中寻找类似于我们在Spring中的内容 - @ConditionalOnMissingBean - 它允许你告诉spring - 只在bean创建时才创建指定缺失。
我尝试过使用扩展程序,看起来可以点击几个事件,然后将它们用于VETO bean。一种方法可能是在此阶段使用BeanManager,并查找已存在的bean,如果它包含您要注入的bean,则VETO这一个。但是,只有当我们看到所有的豆子时,这才有效。
AfterBeanDiscovery看起来合适,但是在调用之前,验证失败,抱怨多个相同类型的bean。
如果我能在这里得到一些帮助,那会很棒。
答案 0 :(得分:2)
你的问题很有趣,可以使用CDI扩展来解决(实际上几乎就像你描述的那样),请参阅下面的一个天真的,有效的,概念验证实现。这是天真的,因为它没有处理,例如生产者方法/领域,可能会缺少更多。
CDI扩展非常强大,但可能相当技术性,所以我们先讨论其他替代方案。
专业化:也许您的用例明确记录您提供SomeService
的默认实施,例如public class SomeServiceDefaultImpl
并按顺序要覆盖它,开发人员应该这样做:
@Specializes
public class SomeServiceSpecialImpl extends SomeServiceDefaultImpl {...}
还要考虑替代方案,如John Ament的评论中所述。
限定符:如果此服务仅在一个地方/几个地方使用且仅在您的代码中使用,则可以使用自定义限定符限定SomeServiceDefaultImpl
,例如{ {1}}。然后注入一个@MyDefaultImpl
,首先查找一个不合格的实例,如果不满足,请查找符合条件的内容:
Instance<SomeService>
提供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();
}
}
的默认实现,以强制代码的客户端提供实现。如果客户想要使用默认值,他们可以简单地使用生产者。
如上所述,下面的实现是一个概念证明:
要求以下注释出现在默认实现中:
@Vetoed
@Target({ TYPE, METHOD, FIELD })
@Retention(RUNTIME)
@Documented
public @interface ConditionalOnMissingBean {
Class<?> value();
}
是必需的,表示“默认”的bean类型。你的实现可以更聪明,即从实际的默认实现中检测bean类型,但是,嘿,这只是一个概念证明!
公然无视生产者!
经过轻微测试,所以可能存在邪恶的角落情况,所以请小心!
除了代码之外,您还需要扩展的所有编排(META-INF / services / javax.enterprise.inject.spi.Extension,beans.xml)。
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);
}
}