• Spring5学习笔记04--Bean后处理器


    内容概要:

    • Bean 后处理器的作用:为 Bean 生命周期各个阶段提供扩展
      • 在beanFactory中补充一些Bean的定义,用于解析 @Autowired @Resource @Value @ConfigurationProperties…
    • 常见的Bean后处理器
      • AutowiredAnnotationBeanPostProcessor : 用于解析 @Autowired @Value 注解
      • CommonAnnotationBeanPostProcessor : 用于解析@Resource @PostConstruct @PreDestroy 注解
      • ConfigurationPropertiesBindingPostProcessor : Spring Boot中的,用于解析 @ConfigurationProperties 注解,该注解用于将指定前缀的配置进行属性绑定

    常见的Bean后处理器

    • BeanFactory 不会做的事
      • 不会主动调用BeanFactory后处理器(用于解析@Bean)
      • 不会主动调用Bean后处理器(用于解析@Autowired、@Resource…)
      • 不会主动初始化单例
      • 不会解析 beanFactory, 还不会解析 ${}#{}
    • 使用 GenericApplicationContext容器来进行测试验证
      • GenericApplicationContext是一个【干净】的ApplicationContext容器
      • 它没有注册(添加)额外的BeanFactory 后处理器、Bean后处理器
    • context.registerBean(beanName, beanClass) : 向 ApplicationContext 里注册Bean
      • 这个方法实现本质就是创建Bean的定义(BeanDefinition),并将BeanDefinition注册到BeanFactory
    • context.refresh() : 初始化容器
      • 作用:
        • 初始化所有单例
        • 执行BeanFactory后处理器和Bean后处理器
      • 在初始化容器前 Bean 和 Bean后处理器只是注册到Bean,并不会实例化对象;初始化容器后,才开始实例化
    • context.close() : 销毁容器
      • 销毁容器前,会执行 @PreDestory 注解的方法
    • BeanFactory里默认的 AutowireCandidateResolver 不会解析@Value, ContextAnnotationAutowireCandidateResolver 可以用来获取@Value中值
    • ${}#{}
    • AutowiredAnnotationBeanPostProcessor : 用于解析 @Autowired @Value 注解
    • CommonAnnotationBeanPostProcessor : 用于解析@Resource @PostConstruct @PreDestroy 注解
      • 会先解析@Resource 再解析@Autowired
    @Slf4j
    static class Bean1 {
    
      private Bean2 bean2;
    
      @Autowired
      public void setBean2(Bean2 bean2) {
        log.debug("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
      }
    
      private Bean3 bean3;
    
      @Resource
      public void setBean3(Bean3 bean3) {
        log.debug("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
      }
    
      private String home;
    
      @Autowired
      public void setHome(@Value("${JAVA_HOME}") String home) {
        log.debug("@Value 生效: {}", home);
        this.home = home;
      }
    
      public Bean1() {
        log.debug("构造 生效");
      }
    
      @PostConstruct
      public void init() {
        log.debug("@PostConstruct 生效");
      }
    
      @PreDestroy
      public void destroy() {
        log.debug("@PreDestroy 生效");
      }
    }
    
    @Slf4j
    static class Bean2 {
    
      public Bean2() {
        log.debug("构造 生效");
      }
    }
    
    @Slf4j
    static class Bean3 {
    
      public Bean3() {
        log.debug("构造 生效");
      }
    }
    
    • 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
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    public static void main() {
      // GenericApplicationContext 是一个【干净】的容器,没有添加额外的BeanFactory后处理器、Bean后处理器,方便测试
      GenericApplicationContext context = new GenericApplicationContext();
    
      // 用原始方法注册三个bean
      context.registerBean("bean1", Bean1.class);
      context.registerBean("bean2", Bean2.class);
      context.registerBean("bean3", Bean3.class);
    
      // BeanFactory不会主动调用Bean后处理器(用于解析@Autowired、@Resource..)(BeanFactory的特点之二)
      // BeanFactory不会解析 `${}`、`#{}`(BeanFactory的特点之三)
    
      // 获取@Value的值
      context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
      // 向BeanFactory中添加解析${} 的解析器
      context.getDefaultListableBeanFactory().addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // 解析 ${}
    
      // 注册Bean后处理器,这个后处理器用于解析@Autowired @Value注解
      context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
      // 注册Bean后处理器,这个后处理器用于解析@Resource @PostConstruct @PreDestroy注解
      context.registerBean(CommonAnnotationBeanPostProcessor.class); // 虽然注册在后面,但是会先解析@Resource 再解析@Autowired
    
      // 初始化容器: 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器
      // 在初始化容器前 Bean 和 Bean后处理器只是注册到Bean,并不会实例化对象
      context.refresh();
    
      // 销毁容器
      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

    输出

    15:09:15.041 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@156643d4
    15:09:15.063 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor'
    15:09:15.081 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.CommonAnnotationBeanPostProcessor'
    15:09:15.089 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
    15:09:15.110 [main] DEBUG com.xxx.TestApplication$Bean1 - 构造 生效
    15:09:15.158 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
    15:09:15.158 [main] DEBUG com.xxx.TestApplication$Bean3 - 构造 生效
    15:09:15.159 [main] DEBUG com.xxx.TestApplication$Bean1 - @Resource 生效: com.huat.lisa.studyspring.s04.S04Application$Bean3@25af5db5
    15:09:15.164 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
    15:09:15.172 [main] DEBUG com.xxx.TestApplication$Bean1 - @Value 生效: /Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/
    15:09:15.174 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
    15:09:15.174 [main] DEBUG com.xxx.TestApplication$Bean2 - 构造 生效
    15:09:15.175 [main] DEBUG com.xxx.TestApplication$Bean1 - @Autowired 生效: com.huat.lisa.studyspring.s04.S04Application$Bean2@a74868d
    15:09:15.175 [main] DEBUG com.xxx.TestApplication$Bean1 - @PostConstruct 生效
    
    Process finished with exit code 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Spring Boot 中的后处理器

    • @ConfigurationProperties 注解可以进行属性绑定,用于将指定前缀的属性绑定到实体上
      • 在初始化之前执行,Bean生命周期中postProcessBeforeInitialization()方法内解析
    • ConfigurationPropertiesBindingPostProcessor : 用于解析 @ConfigurationProperties 注解··
    @ConfigurationProperties(prefix = "java")
    static class Bean4 {
    
      // java.version
      private String version;
    
      // java.home
      private String home;
    
      public String getHome() {
        return home;
      }
    
      public void setHome(String home) {
        this.home = home;
      }
    
      public String getVersion() {
        return version;
      }
    
      public void setVersion(String version) {
        this.version = version;
      }
    
      @Override
      public String toString() {
        return "Bean4{" +
          "version='" + version + '\'' +
          ", home='" + 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
    public static void testConfigurationPropertiesBindingPostProcessor() {
      GenericApplicationContext context = new GenericApplicationContext();
      context.registerBean("bean4", Bean4.class);
    
      // ConfigurationPropertiesBindingPostProcessor 用于属性绑定后处理器, 解析
      ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
    
      context.refresh();
      System.out.println(context.getBean(Bean4.class));
      context.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @Autowired bean后处理器执行分析

    • AutowiredAnnotationBeanPostProcessor 解析 @Autiwired @Value 注解时,核心方法为 postProcessProperties()
    public static void testAutowiredAnnotationBeanPostProcessor() {
      DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      beanFactory.registerSingleton("bean2", new Bean2()); // 注册一个实例化完成的Bean到BeanFactory,这种方式注册会跳过Bean的创建过程、依赖注入、初始化
      beanFactory.registerSingleton("bean3", new Bean3());
      beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 解析@Value
      beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // 解析 ${}
    
      AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
      processor.setBeanFactory(beanFactory);
    
      Bean1 bean1 = new Bean1();
      System.out.println(bean1); // 此时 @Autowired @Value 都没有注入,输出成员变量都为null
    
      // 执行依赖注入 @Autowired @Value
      // 第一个参数PropertyValues组装需要注入的值,非空时,则直接从PropertyValues里获取自动注入的值,不会从BeanFactory里获取
      processor.postProcessProperties(null, bean1, "bean1");
      System.out.println(bean1); // @Autowired @Value 注入成功
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • postProcessProperties()方法源码
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
      // 查找当前类中哪些属性/方法使用了 @Autowired @Value 注解,将对应属性和方法信息 存储到 InjectionMetadata 的 injectedElements 成员变量中
      InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
      try {
        // InjectionMetadata通过反射的方式,设置对应的属性/set方法,进行依赖注入
        metadata.inject(bean, beanName, pvs);
      }
      catch (BeanCreationException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
      }
      return pvs;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 通过反射方式调用 findAutowiringMetadata()metadata.inject() 方法
    public static void testFindAutowiringMetadata() {
      DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      beanFactory.registerSingleton("bean2", new Bean2()); // 注册一个实例化完成的Bean到BeanFactory,这种方式注册会跳过Bean的创建过程、依赖注入、初始化
      beanFactory.registerSingleton("bean3", new Bean3());
      beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 解析@Value
    
      AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
      processor.setBeanFactory(beanFactory);
    
      Bean1 bean1 = new Bean1();
    
      // 通过反射方式调用 processor.findAutowiringMetadata()
      Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
      findAutowiringMetadata.setAccessible(true);
      // 获取bean1 上加了 @Autowired @Value 注解的 成员变量/成员方法 信息,存储到 InjectionMetadata 的 injectedElements 成员变量中
      InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
    
      // 可以通过debug方法看到 metadata里的 injectedElements 成员变量,有两个值,分别是 setBean2 和 setHome
      System.out.println(metadata);
    
      // 调用 InjectionMetadata 来进行依赖注入,注入时按类型查找信息
      metadata.inject(bean1, "bean1", null);
    
      System.out.println(bean1);// @Autowired @Value 成功注入
    }
    
    • 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
    • metadata.inject()方法的核心实现
      • 将待依赖的成员变量/方法信息 封装成 DependencyDescriptor
      • 通过 beanFactory.doResolveDependency() 方法,从BeanFactory中根据类型找到对应的Bean
    public static void injectByField() throws NoSuchFieldException, IllegalAccessException {
      DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      beanFactory.registerSingleton("bean3", new Bean3());
    
      Field field = Bean1.class.getDeclaredField("bean3");
      // 将待依赖的成员变量/方法信息 封装成 DependencyDescriptor
      DependencyDescriptor desc = new DependencyDescriptor(field, true);
    
      // 根据成员变量按类型去找要注入哪个Bean, 后面的三个变量都可以为null, 因为可以根据DependencyDescriptor找到要依赖的对象信息
      Object obj = beanFactory.doResolveDependency(desc, null, null, null);
    
      System.out.println(obj); // 打印bean3
    
      // 通过反射,将bean3设置到Bean1里
      Bean1 bean1 = new Bean1();
      field.setAccessible(true);
      field.set(bean1, obj);
    
      System.out.println(obj); // 成功注入bean3
    }
    
    public static void injectByMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
      DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      beanFactory.registerSingleton("bean2", new Bean2());
    
      Method method = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
      // 将待依赖注入的成员变量/方法信息 封装成 DependencyDescriptor
      // MethodParameter 描述待依赖注入的方法及该方法中第几个变量需要依赖注入
      DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(method, 0), true);
    
      Object obj = beanFactory.doResolveDependency(desc, null, null, null);
      System.out.println(obj);
    
      // 通过反射,将bean2设置到Bean1里
      Bean1 bean1 = new Bean1();
      method.setAccessible(true);
      method.invoke(bean1, obj);
      System.out.println(bean1);// 成功注入bean2
    }
    
    public static void injectByValue(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
      DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 解析@Value
      beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // 解析 ${}
    
      Method method = Bean1.class.getDeclaredMethod("setHome", String.class);
      // 将待依赖注入的成员变量/方法信息 封装成 DependencyDescriptor
      // MethodParameter 描述待依赖注入的方法及该方法中第几个变量需要依赖注入
      DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(method, 0), true);
    
      Object obj = beanFactory.doResolveDependency(desc, null, null, null);
      System.out.println(obj);
    
      // 通过反射,将bean2设置到Bean1里
      Bean1 bean1 = new Bean1();
      method.setAccessible(true);
      method.invoke(bean1, obj);
      System.out.println(bean1);
    }
    
    static class Bean1 {
      // 成员变量依赖注入
      @Autowired
      private Bean3 bean3;
      
      public void setBean3(Bean3 bean3) {
        this.bean3 = bean3;
      }
      
      private Bean2 bean2;
      
      // set方法依赖注入
      @Autowired
      public void setBean2(Bean2 bean2) {
        this.bean2 = bean2;
      }
    }
    
    • 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
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
  • 相关阅读:
    【Ubuntu】安装chrome之后无法启动
    Vue2组件通信 - dispatch 和 broadcast
    Java 网络编程
    C# Winform应用程序简介
    ceph的体系结构
    CentOs7.5 连接不上Xfp,XShell,防火墙未关闭
    这个时代,让我们一起格局打开!【2022戴尔科技峰会预告】
    webpack5基础--02_基本配置( 5 大核心概念)
    平面设计实验二 相册的制作与图层
    Debezium系列之:Debezium Server在生产环境大规模应用详细的技术方案
  • 原文地址:https://blog.csdn.net/qingqingxiangyang/article/details/126667685