我想我解决了这个问题!
考虑以下:
public interface Ambiguity {
public boolean isSatisfiedBy(BeanDefinitionHolder holder);
}
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
public @interface Ambiguous {
Class<? extends Ambiguity> value();
}
@Target(TYPE)
@Retention(RUNTIME)
public @interface Environmental {
public static enum Environment {
Development, Testing, Production
};
Environment[] value() default {};
}
@Named
public class EnvironmentalAmbiguity implements Ambiguity {
/* This can be set via a property in applicationContext.xml, which Spring
can use place holder, environment variable, etc. */
Environment env = Environment.Development;
@Override
public boolean isSatisfiedBy(BeanDefinitionHolder holder) {
BeanDefinition bd = holder.getBeanDefinition();
RootBeanDefinition rbd = (RootBeanDefinition) bd;
Class<?> bc = rbd.getBeanClass();
Environmental env = bc.getAnnotation(Environmental.class);
return (env == null) ? false : hasCorrectValue(env);
}
private boolean hasCorrectValue(Environmental e) {
for (Environment env : e.value()) {
if (env.equals(this.env)) {
return true;
}
}
return false;
}
}
@Named
public class MySuperDuperBeanFactoryPostProcessor implements
BeanFactoryPostProcessor, AutowireCandidateResolver {
private DefaultListableBeanFactory beanFactory;
private AutowireCandidateResolver defaultResolver;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg)
throws BeansException {
if (arg instanceof DefaultListableBeanFactory) {
beanFactory = (DefaultListableBeanFactory) arg;
defaultResolver = beanFactory.getAutowireCandidateResolver();
beanFactory.setAutowireCandidateResolver(this);
return;
}
throw new FatalBeanException(
"BeanFactory was not a DefaultListableBeanFactory");
}
@Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return defaultResolver.getSuggestedValue(descriptor);
}
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder holder,
DependencyDescriptor descriptor) {
Ambiguity ambiguity = getAmbiguity(descriptor);
if (ambiguity == null) {
return defaultResolver.isAutowireCandidate(holder, descriptor);
}
return ambiguity.isSatisfiedBy(holder);
}
private Ambiguity getAmbiguity(DependencyDescriptor descriptor) {
Ambiguous ambiguous = getAmbiguousAnnotation(descriptor);
if (ambiguous == null) {
return null;
}
Class<? extends Ambiguity> ambiguityClass = ambiguous.value();
return beanFactory.getBean(ambiguityClass);
}
private Ambiguous getAmbiguousAnnotation(DependencyDescriptor descriptor) {
Field field = descriptor.getField();
if (field == null) {
MethodParameter methodParameter = descriptor.getMethodParameter();
if (methodParameter == null) {
return null;
}
return methodParameter.getParameterAnnotation(Ambiguous.class);
}
return field.getAnnotation(Ambiguous.class);
}
}
现在,如果我有一个接口 MyInterface 和两个实现它的类 MyFooInterface 和 MyBarInterface ,如下所示:
public interface MyInterface {
public String getMessage();
}
@Named
@Environmental({ Environment.Testing, Environment.Production })
public class MyTestProdInterface implements MyInterface {
@Override
public String getMessage() {
return "I don't always test my code, but when I do, I do it in production!";
}
}
@Named
@Environmental(Environment.Development)
public class DevelopmentMyInterface implements MyInterface {
@Override
public String getMessage() {
return "Developers, developers, developers, developers!";
}
}
如果我想@Inject MyInterface 我会得到与预期相同的多个 bean 定义错误。但是我可以添加 @Ambiguous(EnvironmentalAmbiguity.class) 然后 EnvironmentalAmbiguity 会告诉它满足哪个 bean 定义。
另一种方法是使用 List 并检查它们是否满足给定的 bean 定义,这意味着依赖关系不需要 @Ambiguous 注释。这可能更像是“IoC-ish”,但我也认为它可能表现不佳。我没有测试过。