0

编辑: 也许问这个问题的更简洁的方法是:Spring 是否通过提供我自己的侦听器/工厂/决策逻辑为我提供了一种在注入时解决模棱两可的候选人的方法?


事实上,可以说下面成员字段上的@Environmental 限定符是不必要的:如果@Inject-ion 不明确......让我帮忙?事实上,@ResolveWith(EnvironmentalResolver.class) 也可以。


当 Spring 尝试注入依赖项(使用注释)时,我知道如果我要拥有多个实现该接口的组件,我需要 @Qualifier 一个 @Inject 点。

我想做的是这样的:

class MyFoo implements Foo {
    @Inject
    @Environmental
    private Bar bar;
}

@Environmental(Environment.Production)
class ProductionBar implements Bar {
}

@Environmental({Environment.Dev, Environment.Test})
class DevAndTestBar implements Bar {
}

我希望我需要创建某种歧义解析器,它看起来(模糊地)像这样:

class EnvironmentalBeanAmbiguityResolver {

    // set from configuration, read as a system environment variable, etc.
    private Environment currentEnvironment;

    public boolean canResolve(Object beanDefinition) {
        // true if definition has the @Environmental annotation on it
    }

    public Object resolve(Collection<Object> beans) {
        for (Object bean : beans) {
            // return bean if bean @Environmental.values[] contains currentEnvironment
        }
        throw new RuntimeException(...);
    }
}

这很有用的一个例子是我们有一项联系最终用户的服务。现在我只有一个被破解的 AOP 方面,在调用“MailSender”的方法之前,检查“生产”环境标志,如果没有设置,它会向我们发送电子邮件而不是用户电子邮件。我我不想将其包装在特定于邮件发送的 AOP 方面,而是能够根据当前环境区分服务。有时这只是“生产”或“非生产”的问题,正如我在上面所展示的,但是每个环境的定义也有效。

我认为这也可以用于区域...例如@Regional 和@Regional(Region.UnitedStates) 等等。

我想@Environmental 实际上是一个@Qualifier,如果你想直接依赖于你可以依赖的环境(@Environmental(Production) bean 可能直接依赖于@Environmental(Production) 协作者 - 所以没有歧义较低级别的项目 --- 相同的 @Regional(US) 项目将明确依赖于其他 @Regional(US) 项目,并且会绕过我尚未理解的 BeanAmbiguityResolver)

谢谢。

4

1 回答 1

0

我想我解决了这个问题!

考虑以下:

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”,但我也认为它可能表现不佳。我没有测试过。

于 2011-09-22T22:31:34.843 回答