Bootstrap

自定义BeanPostProcessor实现自动注入标注了特定注解的Bean

  • 定义注解
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnno {
}
  • 定义一个配置类
@Configuration
public class RestConfig {

    @MyAnno
    @Bean
    public PayDTO payDTO(){
        PayDTO payDTO = new PayDTO();
        payDTO.setPayNo("1");
        return payDTO;
    }

    @Bean
    public PayDTO payDTO1(){
        PayDTO payDTO = new PayDTO();
        payDTO.setPayNo("2");
        return payDTO;
    }

}

这个配置类返回了两个PayDTO 类型的对象。

  • 定义使用类
@Component
public class MyConfigTest implements InitializingBean {

    @MyAnno
    @Autowired(required = false)
    private List<PayDTO> payDTOList = Collections.emptyList();

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("payDTO = " + payDTOList);
    }
}

这里想只从容器中获取标注了@MyAnno注解的PayDTO对象,也就是说应该是只能获取到一个。

  • 定义BeanPostProcessor
@Component
public class MyAnnoPostProcesser implements BeanPostProcessor {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //这里只考虑对@Autowired作用于属性上
        Field[] declaredFields = bean.getClass().getDeclaredFields();
        if (bean instanceof MyConfigTest){
            System.out.println("MyConfigTest");
        }
        for (Field field : declaredFields) {
            //判断当前属性是否标记了@MyAnno注解
            MyAnno annotation = field.getAnnotation(MyAnno.class);
            Autowired autowired = field.getAnnotation(Autowired.class);
            if (annotation != null && autowired != null) {
                field.setAccessible(true);
                //获取字段的类型
                Type genericType = field.getGenericType();
                if (genericType instanceof ParameterizedType) {
                    //判断属性类型是否是一个参数化类型,例如 List<String> 这里的String就是泛型参数
                    ParameterizedType parameterizedType = (ParameterizedType) genericType;
                    Type rawType = parameterizedType.getRawType();
                    //泛型原生类型是List类型
                    if (rawType == List.class) {
                        //获取参数化类型列表,每一个参数化列表可能又是一个泛型类型
                        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                        Type argument = actualTypeArguments[0];
                        if (argument instanceof Class) {
                            Class<?> clazz = (Class<?>) argument;
                            //从容器中获取对应此参数化类型并且标注了MyAnno注解的Bean
                            Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(MyAnno.class);
                            if (beansWithAnnotation.size() > 0) {
                                List<Object> beanList = beansWithAnnotation.entrySet().stream().filter(entry -> {
                                    Object value = entry.getValue();
                                    return value.getClass() == clazz;
                                }).map(item -> item.getValue()).collect(Collectors.toList());
                                //进行属性赋值
                                try {
                                    field.set(bean, beanList);
                                } catch (IllegalAccessException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }
                    }
                }
            }
        }
        return bean;
    }
}

BeanPostProcessor实现原理:

  1. 获取Bean示例,并拿到这个Bean对象中声明的属性。

  2. 判断属性是否同时标注了MyAnno和Autowired注解。

  3. 获取属性的类型
    属性的类型返回值有多种情况,可能是Class、ParameterizedType、GenericType等
    如果是一个具体的类型则返回Class,如果是一个泛型类型,例如List,则返回ParameterizedType参数化类型,参数化类型的意思就是使用了泛型类型,并指定了泛型参数,这里的泛型参数就是PayDTO。

  4. parameterizedType.getRawType(); 获取泛型类型的原生类型,也就是List.class

  5. Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); 获取泛型类型的参数类型,例如List类的声明是
    List 这里的T就是泛型参数,而parameterizedType.getActualTypeArguments();返回的是一个数组,因为可能不止一个泛型类型,例如Map<K,V> 。

  6. 获取参数化类型的第一个元素,其实这里返回的就是PayDTO.class对象。

  7. 从容器中获取所有标注了MyAnno注解的Bean对象,并且遍历出是PayDTO类型的。

  8. 通过field.set(bean, beanList);实现属性值注入。

  9. 完成基于BeanPostProcessor实现的自定义注解Bean对象注入。

  • 踩坑

MyConfigTest对象标注的@Component,如果是标注@Configuration注解,此时Spring会生成一个代理对象,通过代理对象拿不到原始对象的相关属性了。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;