请参阅下面的示例,我正在尝试获取 Map
我的TypedService
bean,但如果键是 Type
,我会更喜欢TypeSafeQualifier
中指定的枚举值而不是不安全的 String
“服务名称”。
package org.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Map;
import static org.test.Application.Type.ONE;
import static org.test.Application.Type.TWO;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@SpringBootApplication
public class Application {
@Autowired
Map<String, TypedService> works;
@Autowired
Map<Type, TypedService> fails;
public static void main(String [] args) {
SpringApplication.run(Application.class, args);
}
public enum Type {
ONE,
TWO
}
@Target({TYPE, METHOD, FIELD, CONSTRUCTOR})
@Retention(RUNTIME)
@Qualifier
public @interface TypeSafeQualifier {
Type value();
}
public interface TypedService {
void startSignup();
void activate();
}
@Service
@TypeSafeQualifier(ONE)
public class TypeOneService implements TypedService {
@Override
public void startSignup() {
}
@Override
public void activate() {
}
}
@Service
@TypeSafeQualifier(TWO)
public class TypeTwoService implements TypedService {
@Override
public void startSignup() {
}
@Override
public void activate() {
}
}
}
SpringBoot版本:
springBootVersion=1.5.3.RELEASE
最佳答案
Spring 提供了一种特殊的方法来处理这种类型的注入(inject):AutowireCandidateResolver
.
在您的情况下,代码可能是:
package org.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.LinkedHashMap;
import java.util.Map;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@SpringBootApplication
public class Application {
@Autowired
Map<String, TypedService> works;
@Autowired
Map<Type, TypedService> fails;
@PostConstruct
private void init() {
System.out.println(fails);
}
public static void main(String[] args) {
final SpringApplication application = new SpringApplication(Application.class);
application.addInitializers(context -> {
context.addBeanFactoryPostProcessor(beanFactory -> {
final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
dlbf.setAutowireCandidateResolver(new MyAutowireCandidateResolver(dlbf));
});
});
application.run(args);
}
@QualifierValue(TypeSafeQualifier.class)
public enum Type {
ONE,
TWO
}
@Target({TYPE, METHOD, FIELD, CONSTRUCTOR})
@Retention(RUNTIME)
@Qualifier
public @interface TypeSafeQualifier {
Type value();
}
public interface TypedService {
void startSignup();
void activate();
}
@Service
@TypeSafeQualifier(Type.ONE)
public class TypeOneService implements TypedService {
@Override
public void startSignup() {
}
@Override
public void activate() {
}
}
@Target({TYPE})
@Retention(RUNTIME)
public @interface QualifierValue {
Class<? extends Annotation> value();
}
@Service
@TypeSafeQualifier(Type.TWO)
public class TypeTwoService implements TypedService {
@Override
public void startSignup() {
}
@Override
public void activate() {
}
}
private static class MyAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
private final DefaultListableBeanFactory beanFactory;
private MyAutowireCandidateResolver(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
final Object result = super.getSuggestedValue(descriptor);
if (result != null) {
return result;
}
if (descriptor.getDependencyType() != Map.class) {
return null;
}
final ResolvableType dependencyGenericType = descriptor.getResolvableType().asMap();
final ResolvableType[] typeParams = dependencyGenericType.getGenerics();
final QualifierValue qualifierValue = typeParams[0].getRawClass().getAnnotation(QualifierValue.class);
if (qualifierValue == null) {
return null;
}
final String[] candidateBeanNames = beanFactory.getBeanNamesForType(typeParams[1]);
final LinkedHashMap<Object, Object> injectedMap = new LinkedHashMap<>(candidateBeanNames.length);
for (final String candidateBeanName : candidateBeanNames) {
final Annotation annotation = beanFactory.findAnnotationOnBean(candidateBeanName, qualifierValue.value());
if (annotation == null) {
continue;
}
final Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation, false);
final Object value = annotationAttributes.get("value");
if (value == null || value.getClass() != typeParams[0].getRawClass()) {
continue;
}
injectedMap.put(value, beanFactory.getBean(candidateBeanName));
}
return injectedMap;
}
}
}
首先,我们添加 TypeQualifierValue 注释以使 Spring 知 Prop 有给定类型值的限定符。
二是在main方法中自定义SpringApplication:我们使用BeanFactoryPostProcessor来设置一个自定义的AutowireCandidateResolver。
最后一步:我们编写 MyAutowireCandidateResolver 扩展 ContextAnnotationAutowireCandidateResolver (委托(delegate)而不是继承适用,它甚至更好一点,因为有一天 Spring 可以默认迁移到 `YetAnotherAutowireCandidateResolver')。
这里的关键部分是被覆盖的
getSuggestedValue
方法:在这里我们可以自定义注入(inject)逻辑,考虑依赖的通用类型(字段,方法参数)并应用一些getBean...
- 来自 BeanFactory 的类似方法,带有一些 Spring 的魔力 AnnotationUtils
类(class)。
关于spring - 如何在 Spring 中注入(inject)基于类型安全限定符的 bean 类型映射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45865369/