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

相关推荐
路由侠内网穿透10 小时前
本地部署 GPS 跟踪系统 Traccar 并实现外部访问
运维·服务器·网络·windows·tcp/ip
研华嵌入式17 小时前
如何在高通跃龙QCS6490 Arm架构上使用Windows 11 IoT企业版?
arm开发·windows·嵌入式硬件
带娃的IT创业者21 小时前
Windows 平台上基于 MCP 构建“文心一言+彩云天气”服务实战
人工智能·windows·文心一言·mcp
csdn_aspnet1 天前
Windows Node.js 安装及环境配置详细教程
windows·node.js
摇滚侠1 天前
java语言中,list<String>转成字符串,逗号分割;List<Integer>转字符串,逗号分割
java·windows·list
Source.Liu1 天前
【Pywinauto库】12.2 pywinauto.element_info 后端内部实施模块
windows·python·自动化
Source.Liu1 天前
【Pywinauto库】12.1 pywinauto.backend 后端内部实施模块
开发语言·windows·python·自动化
私人珍藏库1 天前
[Windows] FileOptimizer v17.1.0_一款文件批量压缩工具
windows·批量压缩
掘根1 天前
【CMake】List
windows·microsoft·list
TToolss1 天前
删除文件夹里的网盘图标
windows