实现 MapAutowired
场景
有些时候,在spring注入map的时候,我们不仅仅满足于默认 key 值 为bean的名称,也有可能根据bean中的某个属性值进行注入。这样不得不在容器启动后进行再次处理
目的
实现 根据bean属性注入 map 的 MapAutowired
定义注解
@Target({ ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MapAutowired {
/**
* 属性字段
* @return
*/
String value() default "";
}
实现后置处理器
@Component
@Slf4j
public class MapAutowiredPostProcessor implements InstantiationAwareBeanPostProcessor {
@Autowired
private ApplicationContext applicationContext;
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
Field[] declaredFields = bean.getClass().getDeclaredFields();
if (declaredFields == null || declaredFields.length<1) {
return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
}
for (Field field : declaredFields) {
MapAutowired annotation = field.getAnnotation(MapAutowired.class);
if (annotation != null) {
if (Map.class.isAssignableFrom(field.getType())){
Type type = field.getGenericType();
if (type instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType)type;
Type[] actualTypes = parameterizedType.getActualTypeArguments();
Class<?> valueType = (Class<?>) actualTypes[1];
Map<String, ?> beans = applicationContext.getBeansOfType(valueType);
if (!CollectionUtils.isEmpty(beans)){
field.setAccessible(true);
Object result = null;
if (StringUtils.hasText(annotation.value())){
//如果没有指定key,则注入key为bean名称
//指定key
Map m = new ConcurrentHashMap();
for (Object value : beans.values()) {
Method method = ReflectionUtils.findMethod(valueType, annotation.value());
if (method == null) {
throw new RuntimeException("Method not found: " + annotation.value());
}
Object key = ReflectionUtils.invokeMethod(method, value);
m.put(key, value);
}
result = m;
}else {
// 如果没有指定key,则注入key为bean名称
result = beans;
}
try {
field.set(bean,result);
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to set field value", e);
}
}
}else{
throw new IllegalStateException("Expected a ParameterizedType for the field");
}
}else {
log.error("@MapAutowired can not inject a non Map class, so ignore bean name: {} ", beanName);
}
}
}
return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
}
}
测试
public interface Pay {
Integer getPayType();
void pay();
}
@Component
@Slf4j
public class AliPay implements Pay{
@Override
public Integer getPayType() {
return 1;
}
@Override
public void pay() {
log.info("alipay...");
}
}
@Component
@Slf4j
public class WechatPay implements Pay{
@Override
public Integer getPayType() {
return 2;
}
@Override
public void pay() {
log.info("wechat pay...");
}
}
@Component
@Slf4j
public class ContextPublisher {
/**
* 指定 key 值方法
*/
@MapAutowired(value = "getPayType")
private Map<Integer, Pay> payMap;
@PostConstruct
public void pay(){
System.out.println("pay bean: "+ payMap);
for (Map.Entry<Integer, Pay> entry : payMap.entrySet()) {
log.info("key: {}", entry.getKey());
entry.getValue().pay();
}
}
}
结果:
c.e.b.publisher.ContextPublisher : key: 1
com.example.bootnacos.map.pay.AliPay : alipay...
c.e.b.publisher.ContextPublisher : key: 2
com.example.bootnacos.map.pay.WechatPay : wechat pay...