高级Spring之BeanFactory 后处理器

news2024/11/14 17:19:24

    老样子,直接上代码演示,准备一个干净的容器:


        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        //注册configBean
        context.registerBean("config", Config.class);

        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        // ⬇️销毁容器
        context.close();

   config类:

@Configuration
@ComponentScan("com.tangyuan.a05.component")
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/shopping");
        dataSource.setUsername("root");
        dataSource.setPassword("1234");
        return dataSource;
    }

  
}

       按照代码演示,这里一共有5个Bean,一个Config,一个component包下面的Bean,还有Config类管理的三个bean

     来看输出结果:

     为什么只输出了我们自己编程添加的bean?因为@ComponentScan组件扫描没有生效,@Bean注解也没有生效。

      我们要怎么样才能解析这些注解呢?使用Bean工厂后处理器 

//解析的注解:@ComponentScan @Bean @Import @ImportResource  
context.registerBean(ConfigurationClassPostProcessor.class);

 这时,我们来看输出结果:

 

mapper层接口的扫描: 

 context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner           
        bd.getPropertyValues().add("basePackage", "com.tangyuan.a05.mapper");
        });

 输出结果如下:

 

模拟@ComponentScan注解的解析:

       //查询类上是否有 ComponentScan注解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                   //获取包名
                    System.out.println(p);
                    //转换格式
                    // com.tangyuan.a05.component -> classpath*:com/tangyuan/a05/component/**/*.class
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    System.out.println(path);
                   //读取类上面的源信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                   //通过通配符获取资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    //分析Component注解,生成bean的名称
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    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()));
                        // System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                      
                         //如果直接加了 ComponentScan注解或者间接加了 ComponentScan注解
                       if (annotationMetadata.hasAnnotation(Component.class.getName())
                            || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            //创建BeanDefinition
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                           //生成bean的名称
                            String name = generator.generateBeanName(bd, beanFactory);
                            //添加到bean工厂中
                            beanFactory.registerBeanDefinition(name, bd);
                        }
                    }
                }
            }

输出结果:

     在这里,我们可以将代码抽取出来,整理为一个独立的bean工厂后处理器:

//分析ComponentScan注解,并作Bean定义的补充
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override // context.refresh
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
           //查询类上是否有 ComponentScan注解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                   //获取包名
                    System.out.println(p);
                    //转换格式
                    // com.tangyuan.a05.component -> classpath*:com/tangyuan/a05/component/**/*.class
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    System.out.println(path);
                   //读取类上面的源信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                   //通过通配符获取资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    //分析Component注解,生成bean的名称
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    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()));
                        // System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                      
                         //如果直接加了 ComponentScan注解或者间接加了 ComponentScan注解
                       if (annotationMetadata.hasAnnotation(Component.class.getName())
                            || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            //创建BeanDefinition
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                           //生成bean的名称
                            String name = generator.generateBeanName(bd, beanFactory);
                            //添加到bean工厂中
                            beanFactory.registerBeanDefinition(name, bd);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  使用时,将Bean工厂处理器注册即可:

context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScan

   注:配置类充当的是工厂的角色,配置类中用@Bean标记的方法角色工厂方法
 

解析 @Bean

//@Bean注解
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            //读取配置类的信息
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/tangyuan/a05/Config.class"));
            //获取被注解标注的方法信息
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : methods) {
                System.out.println(method);
                  //获取@Bean注解的属性
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
              //创建  BeanDefinitionBuilder 对象(用工厂方法来创建)
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
               //工厂方法名字
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
              //指定装配模式
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length() > 0) {
                  //初始化方法名字
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                   //注册
                beanFactory.registerBeanDefinition(method.getMethodName(), bd);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  手动添加mapper接口:

@Bean
    public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

    @Bean
    public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

  缺点:不能批量添加

//mapper接口
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            //通配符解析器
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:com/tangyuan/a05/mapper/**/*.class");
            //读取类的源信息
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
           //名字生成器
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            for (Resource resource : resources) {
              //读取信息
                MetadataReader reader = factory.getMetadataReader(resource);
               //读取类源信息
                ClassMetadata classMetadata = reader.getClassMetadata();
                //判断是否是接口
                if (classMetadata.isInterface()) {
                //定义BeanDefinition
                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                               //给构造方法设置参数值
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    //根据mapper接口生成BeanDefinition
                    AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                  //根据bd2生成名字
                 String name = generator.generateBeanName(bd2, beanFactory);
                 //注册
                    beanFactory.registerBeanDefinition(name, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

知识点:

  • ConfigurationClassPostProcessor 可以解析

    • @ComponentScan

    • @Bean

    • @Import

    • @ImportResource

  • MapperScannerConfigurer 可以解析

    • Mapper 接口

收获💡

  1. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能

  2. 这些扩展功能由不同的 BeanFactory 后处理器来完成,其实主要就是补充了一些 bean 定义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182214.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言学习笔记】39. 错误处理、递归

前言 C 语言不提供对错误处理的直接支持&#xff0c;但是作为一种系统编程语言&#xff0c;它以返回值的形式允许您访问底层数据。 错误处理 C 语言不提供对错误处理的直接支持&#xff0c;但是作为一种系统编程语言&#xff0c;它以返回值的形式允许您访问底层数据。在发生…

振弦采集模块配置工具VMTool的MODBUS 工具模块

振弦采集模块配置工具VMTool的MODBUS 工具模块 &#xff08; 1&#xff09; 寄存器查看 此功能模块提供标准的 MODBUS 协议寄存器显示及单个寄存器修改功能&#xff0c;通过点击扩展功能区的【 MODBUS】 标签切换到此模块&#xff0c;如下图所示。 此模块将 VMXXX 所有寄存器以…

Codeforces Round #847 (Div. 3) A~E

比赛链接&#xff1a;Dashboard - Codeforces Round #847 (Div. 3) - Codeforces 目录 A. Polycarp and the Day of Pi B. Taisia and Dice C. Premutation D. Matryoshkas E. Vlad and a Pair of Numbers A. Polycarp and the Day of Pi 题意&#xff1a;求出一个数字…

一步创建 AI 图像网站,即刻生成 AI 图像解决方案 #Graydient

过去一年当中&#xff0c;AI 画图工具非常火爆&#xff0c;一条简单的指令&#xff0c;就能快速得出超高品质的图形&#xff0c;这对于游戏开发者来说无疑是令人振奋的消息&#xff0c;尤其是没有预算和人手打造美术资源的中小团队。从网络上看到的结果来看&#xff0c;AI 的绘…

JDBC(powernode 文档)(内含源代码)

源代码下载地址链接&#xff1a;https://download.csdn.net/download/weixin_46411355/87400304 目录 JDBC概述 1.1 前言 1.2 什么是JDBC 1.3 JDBC的原理 1.4 程序员&#xff0c;JDBC&#xff0c;JDBC驱动的关系及说明 1.4.1 JDBC API 1.4.2 JDBC 驱动 1.4.3 Java程序员…

并发编程-多线程并发设计原理

并发编程-多线程&并发设计原理并发编程简介多线程&并发设计原理1 多线程回顾1.1 Thread和Runnable1.1.1 Java中的线程1.1.2 Java中的线程&#xff1a;特征和状态1.1.3 Thread和Runnable接口1.1.4 Callable1.2 synchronized关键字1.2.1 锁的对象1.2.2 锁的本质1.2.3 实现…

k8s实现controller如何远程调式?

背景&#xff1a; 使用kubebuilder和code-generate生成自定义资源代码后&#xff0c;实现管理自定义资源的controller逻辑。此时&#xff0c;需要调试controller代码逻辑&#xff0c;有2种思路。方法1&#xff1a;对该代码打包成镜像文件&#xff0c;直接部署进入k8s集群中&…

Springboot+vue中小企业合同管理系统

编写企业合同管理系统&#xff0c;让其能创建合同、修改合同、删除合同、合同变更标识、合同收款提醒、合同时间管理、合同废止标识、结束合同、合同统计、合同查询等几大功能。 (1) 创建合同 管理人员将签订后的合同的各项信息存入数据库中&#xff0c;使合同进入开始执行的…

网络编程(2)

封装和分用 1)封装:就是在数据中添加一些辅助传输的信息&#xff1b; 2)分用:就是解析这些信息 3)发送数据的时候&#xff0c;上层协议要把数据交给下层协议&#xff0c;由下层协议来添加一些信息 4)接收数据的时候&#xff0c;下层协议要把数据交给上层协议&#xff0c;有上层…

分割pdf的办法?看这里就明白了!

对于大多数办公党来说&#xff0c;困难的或许不是制作一些办公文件、文档&#xff0c;重要的是如何将这些文档以合适的形式发送给需要的人。不管是客户还是同事、上级&#xff0c;他们对文档格式、内容的要求都是有不一样的标准的。这时候我们就面临一个重要的问题了&#xff0…

Linux驱动开发:块设备驱动

这里写自定义目录标题一、块设备的简介二、块设备驱动框架1、block_device 结构体2、gendisk 结构体3、block_device_operations 结构体4、块设备 I/O 请求过程5、bio 结构体三、使用请求队列方式的块设备驱动程序1、经过第“二”部分的讲解总结&#xff0c;可以得出驱动程序的…

Java基础10:常用API(上)

Java基础10&#xff1a;常用API&#xff08;上&#xff09;一、Math二、System1. currentTimeMillis2. arraycopy三、Runtime四、Object1. toString2. equals3. clone五、Objects六、BigInteger1. 构造方法&#xff08;获取BigInteger&#xff09;2. 常用方法七、BigDecimal1. …

2023年房地产地段研究报告

房地产的投资业务中&#xff0c;选择一个好的地段&#xff0c;或者说区位&#xff0c;是十分重要的。在房地产行业&#xff0c;房价中包含地价&#xff0c;而房价上升的主要原因则是地价的上升。当房屋所处的地段深受消费者青睐、该地段的房屋供不应求时&#xff0c;房屋的价格…

Minecraft 1.19.2 Fabric模组开发 08.3D动画盔甲

我们本次在Fabric 1.19.2中实现具有动画效果的3D盔甲 效果演示效果演示效果演示 1.首先&#xff0c;为了实现这些效果&#xff0c;我们需要首先使用到一个模组:geckolib(下载地址) 找到项目的build.gradle文件&#xff0c;在repositories和dependencies中添加依赖。 reposit…

python+django校园大学生兼职系统vue357

目 录 摘 要 I Abstracts II 目 录 III 第1章 绪论 1 1.1课题背景 1 1.2研究意义 1 1.3研究内容 2 第2章 技术介绍 1 第3章 需求分析 4 3.1需求分析概述 4 3.2可行性分析 4 3.2.1经济可行性 5 3.2.2技术可行性 5 3.3系统功能设计 …

Target 塔吉特DVS EDI 业务测试指南

Target塔吉特是美国仅次于Walmart沃尔玛的第二大巨型折扣零售百货集团&#xff0c;由于拓展了其数字化履约能力&#xff0c;使得越来越多的国内零售产品供应商和Target建立合作关系。Target要求其供应商通过EDI&#xff08;Electronic Data Interchange&#xff0c;中文名称是电…

基于蜣螂算法改进的随机森林回归算法 - 附代码

基于蜣螂算法改进的随机森林回归算法 - 附代码 文章目录基于蜣螂算法改进的随机森林回归算法 - 附代码1.数据集2.RF模型3.基于蜣螂算法优化的RF4.测试结果5.Matlab代码6.Python代码摘要&#xff1a;为了提高随机森林数据的回归预测准确率&#xff0c;对随机森林中的树木个数和最…

来看看这些电脑清理内存的方法

随着电脑使用时间的增加&#xff0c;你有没有发现电脑用得越多反应越慢&#xff1f;如果你遇到这个问题&#xff0c;可以试试这几个优化设置&#xff0c;让你的电脑速度起死回生&#xff01; 方法一&#xff1a;删除临时文件 按键盘上的Win R&#xff0c;在对话框中输入【%temp…

96. 不同的二叉搜索树

96. 不同的二叉搜索树题目算法设计&#xff1a;枚举算法设计&#xff1a;动态规划题目 传送门&#xff1a;https://leetcode.cn/problems/unique-binary-search-trees/ 算法设计&#xff1a;枚举 当 n 5&#xff0c;用 {1、2、3、4、5} BST数是多少组&#xff1f; 有 5 种情…

如何在 macOS 上安装虚拟机软件 VMware Fusion Player (个人版免费)

文章目录IntroVMware 网站注册事宜安装在 VMware Fusion 中创建虚拟机准备 iso 文件VMware Fusion 主界面Intro VMware 网站注册事宜 需要一个邮箱地址&#xff0c;先注册登陆 VMware。 然后在之后某个页面再次 register &#xff0c;就是随意填写一些字段&#xff1a;所在公…