• [spring-04]Bean后处理器


    第四讲:Bean后处理器

    1. Bean 后处理器的作用,为Bean生命周期各个阶段提供扩展
    2. 常见的后处理器

    [015]-常见的后处理器

    定义三个 bean ,名称分别为 Bean1Bean2Bean3

    • 其中bean1中依赖了Bean2Bean3
    • bean2通过@Autowired的注解注入
    • Bean3通过@Resource注解注入
    • 通过@Value注解注入一个Java的环境变量JAVA_HOME的值
    • 定义两个方法init()destroy(),分别加上@PostConstruct@PreDestroy
    • 最后定义一个Bean4——使用ConfigurationProperties

    Bean1

    @Slf4j
    public class Bean1 {
        private Bean2 bean2;
    
        @Autowired
        public void setBean2(Bean2 bean2) {
            log.info("@Autowired 生效:{}", bean2);
            this.bean2 = bean2;
        }
    
        @Autowired
        public void setJava_home(@Value("${JAVA_HOME}") String java_home) {
            log.info("@Value 生效:{}", java_home);
            this.java_home = java_home;
        }
    
        private Bean3 bean3;
    
        @Resource
        public void setBean3(Bean3 bean3) {
            log.info("@Resource 生效:{}", bean3);
            this.bean3 = bean3;
        }
    
        private String java_home;
    
        @PostConstruct
        public void init() {
            log.info("@PostConstruct 生效:{}");
        }
    
        @PreDestroy
        public void destroy() {
            log.info("@PreDestroy 生效:{}");
        }
    
        @Override
        public String toString() {
            return "Bean1{" +
                    "bean2=" + bean2 +
                    ", bean3=" + bean3 +
                    ", java_home='" + java_home + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    Bean2、Bean3、Bea4

    public class Bean2 {
    }
    
    public class Bean3 {
    }
    
    @ConfigurationProperties(prefix = "java")
    @Slf4j
    public class Bean4 {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这里使用GenericApplicationContext来探究@Autowired@Value@Resource@PostConstruct@PreDestroy以及springboot项目中的@ConfigurationProperties这些注解分别是由哪个后处理器来解析的。

    GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试,这里用DefaultListableBeanFactory也可以完成测试,只是会比使用GenericApplicationContext麻烦一些。

    public class TestBeanPostProcessor {
        public static void main(String[] args) {
    
            // GenericApplicationContext 是一个【干净】的容器
            GenericApplicationContext context = new GenericApplicationContext();
    
            // 用原始方法注册三个Bean
            context.registerBean("bean1", Bean1.class);
            context.registerBean("bean2", Bean2.class);
            context.registerBean("bean3", Bean3.class);
            context.registerBean("bean4", Bean4.class);
    
            //解决No qualifying bean of type 'java.lang.String' available
            context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
            // @Autowired @value
            context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
            // @Resource @PostConstruct @PreDestroy
            context.registerBean(CommonAnnotationBeanPostProcessor.class);
            // ConfigurationProperties
            ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
    
            // 初始化容器
            context.refresh();
            System.out.println("Bean4:"+context.getBean(Bean4.class));
            // 销毁容器
            context.close();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述

    结论

    • @Autowired 注解对应的后处理器是AutowiredAnnotationBeanPostProcessor
    • @Value注解需要配合@Autowired注解一起使用,所以也用到了AutowiredAnnotationBeanPostProcessor后处理器,然后@Value注解还需要再用到ContextAnnotationAutowireCandidateResolver解析器,否则会报错;
    • @Resource@PostConstruct@PreDestroy注解对应的后处理器是CommonAnnotationBeanPostProcessor
    • @ConfigurationProperties注解对应的后处理器是ConfigurationPropertiesBindingPostProcessor

    Autowired bean后处理器分析

    @Autowired注解解析用到的后处理器时AutowiredAnnotationBeanPostProcessor

    • 这个后处理器就是通过调用postProcessProperties(PropertyValues pvs, Object bean, String beanName)完成注解的解析和注入的功能

      看源码
      postProcessProperties 这个方法
      	1.方法中又调用了一个私有的方法findAutowiringMetadata(beanName, bean.getClass(), pvs),其返回值InjectionMetadata中封装了被@Autowired注解修饰的属性和方法
      	2.然后会调用InjectionMetadata.inject(bean1, "bean1", null)进行依赖注入
      
      • 1
      • 2
      • 3
      • 4
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 创建过程、依赖注入、初始化
        beanFactory.registerSingleton("bean2",new Bean2());
        beanFactory.registerSingleton("bean3",new Bean3());
        // @Value
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    
        // 1.查找那些属性、方法加了@Autowired,这称为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
        postProcessor.setBeanFactory(beanFactory);
    
        Bean1 bean1 = new Bean1();
        System.out.println("前bean1:" + bean1);
        postProcessor.postProcessProperties(null,bean1,"bean1");
        System.out.println("后bean1:" + bean1);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    反射调用 AutowiredAnnotationBeanPostProcessor类中 findAutowiringMetadata的方法

    在这里插入图片描述

    调用 InjectionMetadata 来进行依赖注入,注入时按类型查找值

    // 2.调用 InjectionMetadata 来进行依赖注入,注入时按类型查找值
    metadata.inject(bean1,"bean1",null);
    System.out.println("----bean1----"+bean1);
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    总代码

    public class DigInAutowired {
        public static void main(String[] args) throws Throwable {
            DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
            // 创建过程、依赖注入、初始化
            beanFactory.registerSingleton("bean2",new Bean2());
            beanFactory.registerSingleton("bean3",new Bean3());
            // @Value
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
            // 设置解析 @Value 注解中的 ${} 表达式的解析器
            beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);
    
            // 1.查找那些属性、方法加了@Autowired,这称为 InjectionMetadata
            AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
            postProcessor.setBeanFactory(beanFactory);
    
            Bean1 bean1 = new Bean1();
            //        System.out.println("前bean1:" + bean1);
            //        postProcessor.postProcessProperties(null,bean1,"bean1");
            //        System.out.println("后bean1:" + bean1);
    
            //--------- 模拟postProcessProperties 调用过程-------
            Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.
                class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
            findAutowiringMetadata.setAccessible(true);
            InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(postProcessor,"bean1",Bean1.class,null);
            System.out.println(metadata);
    
            // 2.调用 InjectionMetadata 来进行依赖注入,注入时按类型查找值
            metadata.inject(bean1,"bean1",null);
            System.out.println("----bean1----"+bean1);
    
            // 3. 如何按照类型查找值
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    InjectionMetadata.inject 分析

    给 bean3 加上 @Autowired 注解

    1. 如何去Bean工厂里面按类型查找值

      由于InjectionMetadata.inject(bean1, “bean1”, null)的源码调用链过长,摘出主要调用过程进行演示

    3.1@Autowired加在成员变量上

    //3.1 @Autowired加在成员变量上,InjectionMetatadata给Bean1注入Bean3的过程
    Field bean3 = Bean1.class.getDeclaredField("bean3");
    bean3.setAccessible(true);
    // 将这个属性封装成一个DependencyDescriptor对象
    DependencyDescriptor dd1 = new DependencyDescriptor(bean3,false);
    // 再执行beanFactory的doResolveDependency
    Bean3 bean3Value = (Bean3)beanFactory.doResolveDependency(dd1,null,null,null);
    System.out.println("bean3:----"+bean3Value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.2 @Autowired加在方法上

    // 3.2 @Autowired加在方法上,InjectionMetatadata给Bean1注入Bean2的过程
    Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
    DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
    Bean2 bean2Value = (Bean2) beanFactory.doResolveDependency(dd2, "bean2", null, null);
    System.out.println("bean2:----"+bean2Value);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.3 @Autowired加在方法上,方法参数为String类型,加了@Value

    // 3.3 @Autowired加在方法上,方法参数为String类型,加了@Value,
    // InjectionMetadata给Bean1注入环境变量JAVA_HOME属性的值
    Method setJava_home = Bean1.class.getDeclaredMethod("setJava_home", String.class);
    DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setJava_home, 0), true);
    String java_home = (String) beanFactory.doResolveDependency(dd3, null, null, null);
    System.out.println("java_home:----"+java_home);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

  • 相关阅读:
    Kotlin 开发Android app(一):Kotlin 建立Android工程
    Pandas数据集的合并与连接merge()方法_Python数据分析与可视化
    基于混沌权重和精英引导的鲸鱼优化算法-附代码
    第2章 持久化初始数据到指定表
    ffmpeg filter amix混音实现
    华为:手机王者归来,汽车起死回生
    mybatis中<if>条件判断带数字的字符串失效问题
    高效工作必备:测试人如何提高沟通技能?
    WPF的简介以及创建
    pytorch常用代码片段
  • 原文地址:https://blog.csdn.net/qq_25614773/article/details/126219194