自定义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会生成一个代理对象,通过代理对象拿不到原始对象的相关属性了。

相关推荐
爱吃火鸡面呀9 小时前
Spark RDD 行动算子(Action)详解
windows·ajax·spark
dlhto9 小时前
Windows 使用 Rufus 制作 AlmaLinux 10.2 启动 U 盘:从 ISO 下载到服务器安装
运维·服务器·windows
小矮强10 小时前
输入凭证也没用?Windows 共享文件夹“来宾访问被安全策略阻止”
windows·经验分享
skywalk816311 小时前
Virtualbox 从7.2.20降级到7.2.18 @FreeBSD系统
windows·virtualbox·freebsd
DogDaoDao11 小时前
Windows 双 Shell 全面对比:CMD 与 PowerShell 的技术剖析
windows·shell·测试·powershell·cmd·程序员开发
长不大的杨瑞杰11 小时前
Windows 安装 OpenCode v2:原生、WSL、Desktop 怎么选?完整配置与避坑指南
人工智能·windows·ai agent·opencode
Misnice12 小时前
Python 内置函数大全
开发语言·windows·python
运维踩坑日记15 小时前
【无标题】
linux·运维·windows
小米渣的逆袭15 小时前
Windows 键盘触发蓝屏获取内存转储(Dump)操作文档
windows