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

  • 定义注解
java 复制代码
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnno {
}
  • 定义一个配置类
java 复制代码
@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 类型的对象。

  • 定义使用类
java 复制代码
@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
java 复制代码
@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会生成一个代理对象,通过代理对象拿不到原始对象的相关属性了。

相关推荐
戒了,最后一次7 小时前
DeepSeek Harness 源码安装教程(Windows 篇)
windows·腾讯云·deepseek·harness
Flag在我手里8 小时前
应急响应-Windows权限维持
windows·rdp·应急响应·玄机
SWAGGY..11 小时前
【C++初阶】:(14)从底层结构理解 deque——为什么它成为 stack 和 queue 的默认容器
windows
呉師傅11 小时前
关于惠普LaserJet1018打印机在Windows11上打印颜色偏淡的问题解决方法
运维·服务器·windows·计算机外设·电脑
永不复还13 小时前
Windows驱动开发:IRP、完成例程与 MDL 生命周期深度解析
windows·驱动开发
百事牛科技13 小时前
只可读不可改!PDF禁止修改实操方法
windows·pdf
老王的笔记v13 小时前
42期 鼠鼠格式转换工具整合图片文档音视频转换,Windows和Mac免费使用
windows·开源·github·音视频
可爱系程序猿14 小时前
Windows 11 添加共享打印机提示 0x0000011b:从 RPC 到驱动架构排查
windows·rpc·架构
渣渣盟14 小时前
Nginx 从零到上手:Windows & Linux 双环境教程
linux·windows·nginx
魂祈梦14 小时前
windows终端乱码问题
windows