【问题标题】:Spring - Qualify injection candidates by designated environmentSpring - 通过指定环境限定注入候选者
【发布时间】:2011-09-22 17:02:07
【问题描述】:

编辑: 也许问这个问题的更简洁的方法是: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)

谢谢。

【问题讨论】:

    标签: spring dependency-injection qualifiers


    【解决方案1】:

    我想我解决了这个问题!

    考虑以下几点:

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

    【讨论】:

      猜你喜欢
      • 2018-02-08
      • 1970-01-01
      • 2014-03-30
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 2019-12-17
      • 2017-10-10
      • 2017-07-28
      相关资源
      最近更新 更多