• Spring5学习笔记05--BeanFactory后处理器


    内容概要:

    • BeanFactory 后处理器的作用: 为 BeanFactory 提供扩展
      • 为 BeanFactory 补充一些bean的定义,用于解析 @Configuration @Component @Bean…
    • 常见的 BeanFactory 后处理器
      • ConfigurationClassPostProcessor
    • 准备工作,定义Bean 以及配置类
    @Configuration
    @ComponentScan("com.spring.learn.component")
    public class Config {
    
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }
    
    @Slf4j
    class Bean1 {
    
        public Bean1() {
            log.debug("初始化 Bean1");
        }
      
    }
    
    // com.spring.learn.component 包下的 Bean2 Bean3 Bean4
    @Slf4j
    @Component
    public class Bean2 {
    
        public Bean2() {
            log.debug("初始化 Bean2");
        }
    }
    
    @Slf4j
    @Controller
    public class Bean3 {
    
        public Bean3() {
            log.debug("初始化 Bean3");
        }
    }
    
    @Slf4j
    public class Bean4 {
    
        public Bean4() {
            log.debug("初始化 Bean4");
        }
    }
    
    • 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

    ConfigurationClassPostProcessor

    • 直接注册Config类,没有添加BeanFactory 后处理器
      • config里的 @ComponentScan @Bean 注解都没有解析生效
    public static void test(String[] args) {
      GenericApplicationContext context = new GenericApplicationContext();
      context.registerBean("config", Config.class);
    
      // 初始化容器
      context.refresh(); // 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器
    
      // 只有config的BeanDefinition, config里的 @ComponentScan @Bean 注解都没有解析生效
      for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName); // 只有config
      }
    
      // 销毁容器
      context.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • ConfigurationClassPostProcessor: BeanFactory 后处理器,可以解析 @ComponentScan @Bean @Import @ImportResource…等注解
    public static void testConfigurationClassPostProcessor(String[] args) {
      GenericApplicationContext context = new GenericApplicationContext();
      context.registerBean("config", Config.class);
      // 添加BeanFactory后处理器,可以解析 @ComponentScan @Bean @Import @ImportResource...
      context.registerBean(ConfigurationClassPostProcessor.class);
    
      // 初始化容器
      context.refresh(); // 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器
    
      // 5个的BeanDefinition,config里的 @ComponentScan @Bean 注解有解析生效
      for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName); // config、ConfigurationClassPostProcessor、bean2、bean3、bean1
      }
    
      // 销毁容器
      context.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    @ComponentScan 注解解析执行过程

    • 以解析 @ComponentScan 为例,分析ConfigurationClassPostProcessor BeanFactory 后处理器的执行过程
    • @ComponentScan: 扫描指定包下添加了 @Component 注解的Bean,并将Bean管理到Spring容器中(即将Bean的定义注册到BeanFactory里)
      • 根据配置的basePackage ,Spring 最终是以文件的方式获取资源,如 com.spring.learn.component -> classpath*:com/spring/learn/component/**/*.class (** 是指所有子包)
    public static void testComponentScan(String[] args) throws IOException {
      GenericApplicationContext context = new GenericApplicationContext();
      context.registerBean("config", Config.class);
    
      // 查找某各类上有没有指定的注解
      ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
    
      // 以解析 @ComponentScan 为例,扫描指定包下添加了 @Component 注解的Bean,并将Bean管理到Spring容器中(即将Bean的定义注册到BeanFactory里)
      if (componentScan != null) {
        // 获取 @ComponentScan 扫描的包,扫描包下的所有class文件
        for (String basePackage : componentScan.basePackages()) {
          System.out.println(basePackage);
    
          // Spring 最终是以文件的方式获取资源,如 com.spring.learn.component -> classpath*:com/spring/learn/component/**/*.class (** 是指所有子包)
          String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
          System.out.println(path);
          // 用于读取类(*.class文件)的元信息,包括类的名字、注解等信息
          CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
          // 获取文件资源
          Resource[] resources = context.getResources(path);
    
          // 根据注解获取BeanName,向BeanFactory中注册Bean的定义时,需要beanName,所以用到了这个类
          AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
          DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
    
          for (Resource resource : resources) {
            System.out.println(resource);
            MetadataReader reader = factory.getMetadataReader(resource);
            System.out.println("类名:" + reader.getClassMetadata().getClassName());
            // 判断类上是否直接添加了指定注解
            AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
            System.out.println("是否添加了 @Component 注解:" + annotationMetadata.hasAnnotation(Component.class.getName()));
            // 判断类上是否添加了指定注解的派生注解(只添加了注解,该方法返回false)
            // 派生注解: 如 @Controller 注解上有 @Component,则类上添加 @Controller 注解就是添加了 @Component 派生注解
            System.out.println("是否添加了 @Component 派生注解:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
    
            if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
              // 生成当前类的BeanDefinition,将Bean的定义注册到BeanFactory里
              AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
              String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
              beanFactory.registerBeanDefinition(beanName, beanDefinition);
            }
          }
        }
    
      }
    
      // 初始化容器
      context.refresh(); // 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器
    
      System.out.println(">>>>>>>>>>");
      // 3个的BeanDefinition,@ComponentScan 注解有解析生效
      for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName); // config bean2 bean3
      }
    
      // 销毁容器
      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
    • 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
    自定义 @ComponentScan 注解解析的BeanFactory后处理器
    • 方式一: 实现 BeanFactoryPostProcessor, 重写postProcessBeanFactory() 方法,实现上面的功能
      • postProcessBeanFactory() 在执行context.refresh()时会回调这个方法调用
    • 方式二:实现 BeanFactoryPostProcessor 的子接口 BeanDefinitionRegistryPostProcessor, 重写 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法
    /**
     * 根据 TestBeanFactoryPostProcessor.testComponentScan() 方法,自定义一个解析 @ComponentScan 注解的BeanFactory后处理器
     * 实现 BeanFactoryPostProcessor(或者其子接口 BeanDefinitionRegistryPostProcessor), 重写postProcessBeanFactory() 方法,在执行context.refresh()时会回调这个方法调用
     */
    public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
    
        /**
         * 执行时机: 执行context.refresh()时,会调用该方法
         * @param configurableListableBeanFactory
         * @throws BeansException
         */
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    
            try {
                // 查找某各类上有没有指定的注解
                ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
    
                // 以解析 @ComponentScan 为例,扫描指定包下添加了 @Component 注解的Bean,并将Bean管理到Spring容器中(即将Bean的定义注册到BeanFactory里)
                if (componentScan != null) {
                    // 获取 @ComponentScan 扫描的包,扫描包下的所有class文件
                    for (String basePackage : componentScan.basePackages()) {
                        System.out.println(basePackage);
    
                        // Spring 最终是以文件的方式获取资源,如 com.spring.learn.component -> classpath*:com/spring/learn/component/**/*.class (** 是指所有子包)
                        String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
                        // 用于读取类(*.class文件)的元信息,包括类的名字、注解等信息
                        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                        // 获取文件资源
                        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
    
                        // 根据注解获取BeanName,向BeanFactory中注册Bean的定义时,需要beanName,所以用到了这个类
                        AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
                        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
    
                        for (Resource resource : resources) {
                            MetadataReader reader = factory.getMetadataReader(resource);
                            AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                            // 判断类上是否直接添加了指定注解 / 派生注解
                            // 派生注解: 如 @Controller 注解上有 @Component,则类上添加 @Controller 注解就是添加了 @Component 派生注解
                            if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                                // 生成当前类的BeanDefinition,将Bean的定义注册到BeanFactory里
                                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
                                String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
                                beanFactory.registerBeanDefinition(beanName, beanDefinition);
                            }
                        }
                    }
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    • 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

    @Bean 注解解析执行过程

    • 添加 @Configuration 注解的类可以看做是个Bean工厂类, @Bean注解标注的方法充当的就是工厂方法
    • 下面这种方式生成BeanDefinition, Bean初始化是直接通过默认构造器创建的
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanMethod.getReturnTypeName()).getBeanDefinition();
    
    • 1
    • 如果要使用工厂方法来创建Bean,要设置工厂方法名 以及 工厂类名
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition()
                        .setFactoryMethodOnBean(factoryMethodName, factoryBeanName)
                        .getBeanDefinition();
    
    • 1
    • 2
    • 3
    • @Bean 方式可以传入参数,解析这个参数需要在 BeanDefinitionBuilder 中指定自动装配模式
    build.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    
    • 1
    • 配置类
    @Configuration
    @ComponentScan("com.huat.lisa.studyspring.s05.component")
    public class Config {
    
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    
        @Bean(initMethod = "init")
        public DruidDataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl("jdbc:mysql://localhost:3306/test");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSource);
            return sqlSessionFactoryBean;
        }
    }
    
    • 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
    public static void testBean(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
    
        // 以解析 @Bean 为例, 如何将Bean添加到Spring容器中进行管理,又如何初始化以及获取Bean对象
        // 根据class文件读取 @Configuration 注解标注的类(Config类)的元信息,根据配置类元信息,获取所有 @Bean 注解标注的方法
        // 这种方式获取类信息不会使用反射,效率更高,更推荐这种方式
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/huat/lisa/studyspring/s05/Config.class"));
    
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
    
        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
        // 获取使用 @Bean 注解标注的所有方法,将方法返回的Bean注册到BeanFactory
        Set<MethodMetadata> beanMethods = annotationMetadata.getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata beanMethod : beanMethods) {
            System.out.println(beanMethod + "--" + beanMethod.getDeclaringClassName() + "--" + beanMethod.getMethodName() + "--" + beanMethod.getReturnTypeName());
            String initMethodName = beanMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
            // 设置工厂方法名 工厂类名,创建Bean的定义,告诉BeanFactory要通过哪个工厂方法来创建Bean
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition()
                .setFactoryMethodOnBean(beanMethod.getMethodName(), "config")
                .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); // 设置自动装配模式
            if (initMethodName.length() > 0) {
                builder.setInitMethodName(initMethodName); // 设置初始化方法
            }
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            beanFactory.registerBeanDefinition(beanMethod.getMethodName(), beanDefinition);
        }
      
        // 初始化容器,用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器
        context.refresh();
    
        // config bean1 sqlSessionFactoryBean dataSource, 其中sqlSessionFactoryBean设置dataSource成功,dataSource设置db连接信息成功,也执行了init方法
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    
        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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    二维码智慧门牌管理系统升级解决方案:轻松实现辖区范围门址统计
    前端开发语言有哪些
    【编程之路】面试必刷TOP101:动态规划(入门)(62-66,Python实现)
    【前端】Vue+Element UI案例:通用后台管理系统-面包屑、tag栏
    vulnhub靶场之BLUESMOKE: DEVRANDOM2|bluesmoke
    docker(一) 安装与创建容器
    约束(constraint)
    SAP Router 配置手册
    JavaScript DOM API中append和appendChild的不同点
    12.HTTPS
  • 原文地址:https://blog.csdn.net/qingqingxiangyang/article/details/126751553