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

相关推荐
w***74408 小时前
SQL Server2022版详细安装教程(Windows)
windows
IT逆夜15 小时前
实现Yum本地仓库自动同步的完整方案(CentOS 7)
linux·运维·windows
v***598315 小时前
DeepSeek API 调用 - Spring Boot 实现
windows·spring boot·后端
q***25117 小时前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
love530love20 小时前
【笔记】ComfUI RIFEInterpolation 节点缺失问题(cupy CUDA 安装)解决方案
人工智能·windows·笔记·python·插件·comfyui
M***Z2101 天前
如何在Windows系统上安装和配置Node.js及Node版本管理器(nvm)
windows·node.js
love530love1 天前
【保姆级教程】Windows + Podman 从零部署 Duix-Avatar 数字人项目
人工智能·windows·笔记·python·数字人·podman·duix-avatar
tobebetter95271 天前
How to manage python versions on windows
开发语言·windows·python
q***71851 天前
windows下安装并使用node.js
windows·node.js