Spring原理学习(三):BeanFactory后处理器原理解析与模拟实现

一、简单认识BeanFactory后处理器

1.1 BeanFactory后处理器的作用 

        接前文:Spring原理学习(一):BeanFactory和ApplicationContext的原理和实现 我们已经简单介绍了 BeanFactory后处理器 的作用,今天我们先再来再次体验一下。

        先准备一个主启动类: 

public class A05 {
    private static final Logger log = LoggerFactory.getLogger(A05.class);

    public static void main(String[] args) throws IOException {

        // GenericApplicationContext 是一个【干净】的容器,没有spring添加的各种后处理器
        GenericApplicationContext context = new GenericApplicationContext();

        //注册config
        context.registerBean("config", Config.class);

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

        // 看看容器里被注册的bean有哪些
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        // 销毁容器
        context.close();
    }
}

         然后准备一个bean,上面加了@Configuration和 @Bean 注解;并且加上了@ComponentScan 注解,应该能扫描到加有@Component注解的Bean2。

@Configuration
@ComponentScan("com.itheima.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/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
}
@Component
public class Bean2 {

    private static final Logger log = LoggerFactory.getLogger(Bean2.class);

    public Bean2() {
        log.debug("我被 Spring 管理啦");
    }
}

         所以config容器里应该有5个bean:config本身、扫描到的bean2、自己管理的三个(bean2、sqlSessionFactory、druidDataSource)。

        但是运行之后我们发现:

        只有我们自己添加的一个bean。 这说明执勤啊提到的注解都没有被解析。而解析这些注解的功能就是beanFactory后处理器提供的。

1.2  ConfigurationClassPostProcessor 后处理器

         我们加上ConfigurationClassPostProcessor 后处理器就可以解析这些注解了。

public class A05 {
    private static final Logger log = LoggerFactory.getLogger(A05.class);

    public static void main(String[] args) throws IOException {

        // GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
    
        //解析@ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);

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

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

        // 销毁容器
        context.close();
}

        运行结果:

1.3 MapperScannerConfigurer 后处理器 

        再加上ConfigurationClassPostProcessor 后处理器就可以解析这些注解了 

public class A05 {
    private static final Logger log = LoggerFactory.getLogger(A05.class);

    public static void main(String[] args) throws IOException {

        // GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
    
        //解析@ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);
        //解析@MapperScanner
        context.registerBean(MapperScannerConfigurer.class, bd -> { 
            bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
        }); 

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

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

        // 销毁容器
        context.close();
}

        Mapper1 和 Mapper2加上了@Mapper注解,Mapper3没有 

@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
public class Mapper3 {
}

         运行结果:

二、模拟实现后处理器——解析@ComponentScan

        首先要明确执行的步骤:

  1. 先找到加有@Component注解的是哪些包,并拿到包下的资源
  2. 找到包下有哪些bean加上了@Component注解或派生注解
  3. 生成这些资源对应的beanDefinition和beanName
  4. 注册进beanFactory
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 = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                    //打印出来之后是com.itheima.a05.component这种格式的
                    System.out.println(p);
                    //我们要做格式转换 com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    //打印出来之后是classpath*:com/itheima/a05/component/**/*.class
                    System.out.println(path);
                    //CachingMetadataReaderFactory用于读取类的源信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    //getResources用于获取类路径下的所有资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        // 读取类的源信息
                        MetadataReader reader = factory.getMetadataReader(resource);
                        // 读取类上的注解信息
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        //是否加了 @Component 注解
                        boolean hasAnnotation = annotationMetadata.hasAnnotation(Component.class.getName());
                        //是否加了 @Component 派生注解,比如@Controller
                        boolean hasMetaAnnotation = annotationMetadata.hasMetaAnnotation(Component.class.getName());
                        if (hasAnnotation || hasMetaAnnotation) {
                            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();
        }
    }
}

         运行结果:

image-20220329192201153 

三、模拟实现后处理器——解析@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();
            //这里为了省事,写死了路径,后续优化可以考虑先找找有@Component注解的包有哪些,然后逐一读取
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : methods) {
                System.out.println(method);
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                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();
        }
    }
}

 四、模拟实现后处理器——MapperScannerConfigurer  

        需要注意的是,我们要注册进spring中的是MapperFactoryBean,而不是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/itheima/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()) {
                    //这里要注意,交给spring管理的类型是MapperFactoryBean,而不是Mapper
                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    //bd2仅仅是为了生成名字,spring内部也是这么做的
                    AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                    String name = generator.generateBeanName(bd2, beanFactory);
                    beanFactory.registerBeanDefinition(name, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

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

    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

酒店拥有VR全景是一种什么样的体验?

每一家酒店都希望自己门庭若市&#xff0c;有更多的人来&#xff0c;随着信息化和互联网的发展时代的到来&#xff0c;酒店营销也逐渐加入了更多的现代元素&#xff0c;那么&#xff0c;酒店怎么样更好地利用互联网来做宣传、来获得更多的客户呢&#xff1f;VR全景作为新兴的富…

排序和分页

排序和分页一、排序1.简单用法3.不同字段不同排序现实二、分页1.简单分页2.order by 配合limit三、分页8.0新特性1.offset总结提示&#xff1a;以下是本篇文章正文内容 一、排序 1.简单用法 select employee_id,last_name,salary from employees order by salary;默认是升序…

Maven高级-分模块开发依赖管理

Maven高级-分模块开发&依赖管理1&#xff0c;分模块开发1.1 分模块开发设计1.2 分模块开发实现1.2.1 环境准备1.2.2 抽取domain层步骤1:创建新模块步骤2:项目中创建domain包步骤3:删除原项目中的domain包步骤4:建立依赖关系步骤5:编译maven_02_ssm项目步骤6:将项目安装本地…

Memory Map

主要介绍AM64x的MSRAM和DDR的内存分布&#xff1a; MSRAM:总共2MB,被分成8个banks,每个256KB。 首先了解一下&#xff0c;两种Domain: In TI documentation, the MCU Domain may be referred to as “M4FSS Island”, “MCU Island”, “MCU Channel”, or “MCU Subsystem…

Redis分布式缓存

文章目录一、 概述1. 单节点Redis存在的问题2. 单节点Redis问题针对解决方案二、Redis持久化1. RDB持久化2.RDB异步持久化原理介绍3. AOF持久化4. ROB和AOF对比三、Redis主从架构1. 搭建主从架构2. 主从数据同步原理四、Redis哨兵1. 哨兵的作用和原理2.搭建哨兵集群3. RedisTem…

Linux 操作系统原理 — RSS 多队列网卡

目录 文章目录目录RSS 多队列网卡RSS 技术实现原理RSS FilterRSS HASH硬中断信号绑定ethtool 操作指令RSS 多队列网卡 在以往&#xff0c;一张 NIC 只具有一个 Rx Queue&#xff0c;对应一个 CPU Core 来进行收包处理。在多核时代&#xff0c;为了充分利用 Multi-CPU Cores&am…

如何使用pandas提取含有指定字符串

这里写自定义目录标题name age state point0 Alice 24 NY 641 Bob 42 CA 922 Charlie 18 CA 70name age state point0 Alice 24 NY 642 Charlie 18 CA 700 False1 True2 TrueName: state, dtype: boolname age state point1 Bob 42 CA 922 Charlie 18 CA 700 True1 False2 True…

tmall.service.settleadjustment.modify( 修改结算调整单 )

&#xffe5;开放平台免费API必须用户授权 提供给服务商在对结算有异议时&#xff0c;发起结算调整单。 通过说明调整单ID&#xff0c;调整费用值&#xff0c;调整原因进行结算调整单修改。 公共参数 请求地址: 公共请求参数: 公共响应参数: 请求参数 响应参数 点击获取key和…

MyBatisPlus-DML编程控制

MyBatisPlus-DML编程控制4&#xff0c;DML编程控制4.1 id生成策略控制知识点1&#xff1a;TableId4.1.1 环境构建4.1.2 代码演示AUTO策略步骤1:设置生成策略为AUTO步骤2:删除测试数据并修改自增值步骤3:运行新增方法INPUT策略步骤1:设置生成策略为INPUT步骤2:添加数据手动设置I…

【hello Linux】Linux权限管理

目录 1.shell命令以及运行原理 2. Linux权限的概念 3. Linux权限管理 3.1 文件访问者的分类 3.2 文件类型 3.3 访问权限 3.4 访问权限的表示方法 4. 访问权限的相关设置 4.1 chmod命令&#xff1a;修改权限 4.2 chown命令&#xff1a;修改文件的拥有者 4.3 chgrp 命令&#xff…

idea中的项目上传gitee

1.把gitee插件安装找重启idea 2.打开gitee网站从设置->私有令牌 获取token信息完成登录 复制到idea中点击log in 点击ok 3.把项目转为git管理 4.上传到gitee 5.去刷新gitee仓库此时就会发现多了一个我们的项目 以下是拷贝新项目到idea操作 http://t.csdn.cn/ycnSX

【Ansys】什么软件模块是DS,它和workbench、mechanical的区别在哪里?

一、DesignSpace和workbench 早期的Workbench称之为DesignSpace&#xff0c;更偏向于建模。 现在DS是license的一种&#xff0c;而分析的模块在11中称之为Simulation&#xff08;Design Simulation&#xff09;&#xff0c;在12中改名为Mechanical。 所以&#xff0c;你可以…

IFPUG功能点度量4:度量事务功能

一、基本概念 1、事务功能 事务功能是处理数据功能的基本过程。 每个事务功能都是一个基本过程。 事务功能由多个逻辑处理来完成。 事务功能包含三种类型&#xff1a;EI、EO、EQ 2、基本过程 一个基本过程是由一个逻辑处理或者多个逻辑处理来完成的。 如何识别&#xf…

【备考技巧】系统集成(案例分析题)并不难

案例分析考题分析&#xff1a; 正常情况下四道题&#xff0c;计算题20分左右&#xff0c;剩余三道分析题每题15-20分左右。 ☆案例分析题&#xff1a; 从整体管理、范围管理、质量管理、人力资源管理、合同管理&采购管理、配置管理、风险管理、进度管理、成本管理中出简答题…

Jvm学习笔记(二)GC

GC 垃圾收集(GC)起源于Lisp&#xff0c;远比Java的历史更久&#xff0c;它主要思考了三件事情&#xff1a; 哪些内存需要回收&#xff1f;什么时候回收&#xff1f;如何回收&#xff1f; 本章就根据这三个点进行分析。 哪些内存需要回收&#xff1f; 在java堆中存放着无数的…

Mac浏览器无法上网但可以用微信等

可以使用微信QQ等&#xff0c;但是浏览器无法上网&#xff0c;Mac浏览器无法上网怎么办&#xff0c;Mac浏览器无法上网但可以用微信等&#xff08;百度了一下&#xff0c;没有找到原因是为什么&#xff0c;只找到了解决方法&#xff0c;记录一下&#xff09; 1.首先我们打开Ma…

Centos安装docker以及通过docker部署Mysql,照做就行!

1.安装docker 1.1给虚拟机联网&#xff08;反斜杠带表该语句没写完&#xff09; yum install -y yum-utils \device-mapper-persistent-data \lvm2 --skip-broken 1.2更新本地文件镜像 # 设置docker镜像源 yum-config-manager \--add-repo \https://mirrors.aliyun.com/doc…

【Python_Selenium学习笔记(四)】基于Selenium模块实现键盘操作

基于Selenium模块实现键盘操作 前言 在 Selenium 模块中&#xff0c;提供了一个 Keys 类&#xff0c;来处理键盘操作&#xff1b; 在 Selenium 模块中&#xff0c;使用 send_keys() 方法&#xff0c;来模拟键盘输入&#xff0c; 此篇文章主要介绍如何使用 Keys 类 和 send_ke…

vue请求本地JSON文件(注意路径 否则会404)

npm i axios // main.jsimport axios from "axios";Vue.prototype.$axios axios; //全局注册&#xff0c;使用方法为:this.$axios...vue/cli 2 json文件存放目录为 根目录下static/json/aaa.json // 使用getVideoData() {this.$axios.create({baseURL: "&quo…

springboot 部署k8s(二)

系列文章目录 目录 系列文章目录 前言 操作步骤 1.springboot.yaml文件 2.查看deployment 3.查看service服务 4.验证服务 总结 前言 springboot 部署到k8s 上。里面涉及了deployment, Service, NodePort. 操作步骤 1.springboot.yaml文件 apiVersion: apps/v1 kind: …