spring整合mybatis的底层原理

news2024/11/16 17:37:04

spring整合mybatis的底层原理

原理:

  1. FactoryBean的自定义对象
  2. jdk动态代理Mapper接口对象

一、手写一个spring集成mybatis

目录结构:
在这里插入图片描述

1.1 入口类
public class Test {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.refresh();

        UserService userService = (UserService)context.getBean("userService");
        userService.test();
    }
}
1.2 配置类
@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper")
@ComponentScan("com.athome.tulin.springmybatis")
public class AppConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws IOException {
        System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………");
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }
}
1.3 业务类
@Component
public class UserService {

    public UserService() {
        System.out.println("3.…………创建UserService…………");
    }

    //如何把mybatis生成的UserMapper的代理对象赋值给UserMapper
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private MemberMapper memberMapper;

    public void test(){
        System.out.println("7.……UserService…test…");
        System.out.println(userMapper.selectById());
        System.out.println(orderMapper.selectById());
        System.out.println(memberMapper.selectById());
    }
}
1.4 创建3个Mapper接口
public interface MemberMapper {

    @Select("select 'member' ")
    String selectById();
}
public interface OrderMapper {
    @Select("select 'order' ")
    String selectById();
}
public interface UserMapper {
    @Select("select 'user' ")
    String selectById();
}
1.5 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(CondorHeroImportBeanDefinitionRegistrar.class)
public @interface CondorHeroMapperScan {
    String value();
}
1.6 自定义fanctoryBean
public class CondorHeroFactoryBean implements FactoryBean {
    
    private Class mapperInterface;

    private SqlSession sqlSession;
    public CondorHeroFactoryBean(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }
    /**
     * 从容器查找SqlSessionFactory 并获取sqlSession 赋值于sqlSession
     * 扫描的时候有 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
     * 那他就会自动找set方法
     * @param sqlSessionFactory
     */
    public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
        System.out.println("5.……setSqlSession……");
        sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
        this.sqlSession = sqlSessionFactory.openSession();
    }
    
    @Override
    public Object getObject() throws Exception {
        //动态代理获取UserMapper接口对象
        System.out.println("6.……getObject……");

       return sqlSession.getMapper(mapperInterface);
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }
}
1.7 自定义Bean注册类
public class CondorHeroImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        System.out.println("1.……registerBeanDefinitions……");
        //1.获取注解上的指定路径
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(CondorHeroMapperScan.class.getName());
        String path = (String)annotationAttributes.get("value");
        //2.扫描
        CondorHeroBeanDefinitionScanner scanner = new CondorHeroBeanDefinitionScanner(registry);
       scanner.addIncludeFilter(new TypeFilter() {
           @Override
           public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
               return true;
           }
       });

        scanner.scan(path);
    }
}
1.8 自定义扫描类
public class CondorHeroBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    public CondorHeroBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)  {
        //只关心接口(判断是否是接口)
       return beanDefinition.getMetadata().isInterface();
    }

    /**
     * 扫描路径并得到beanDefinition
     * @param basePackages
     * @return
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        System.out.println("2.……doScan……");
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);

        for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) {
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanDefinitionHolder.getBeanDefinition();
            //设置值
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
            //设置名称
            beanDefinition.setBeanClassName(CondorHeroFactoryBean.class.getName());

            //将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找
            beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
        return beanDefinitionHolders;
    }
}

运行结果是:
1.……registerBeanDefinitions……
2.……doScan……
3.…………创建UserService…………
4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………
5.……setSqlSession……
6.……getObject……
5.……setSqlSession……
6.……getObject……
5.……setSqlSession……
6.……getObject……
7.……UserService…test… user order member

二、原理解析

2.1 通过@MapperScan导入了MapperScannerRegistrar类

在这里插入图片描述

2.2 MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法

在这里插入图片描述

2.3 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition

在这里插入图片描述

2.4 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法

在这里插入图片描述

2.5 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描(scanner.scan)
2.6 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition

在这里插入图片描述

2.7 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      //设置值
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      //设置名称
      definition.setBeanClass(this.mapperFactoryBeanClass);

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory",
            new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate",
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        //将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找
       definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }
2.8 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
2.9 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}
 @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  @Override
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }

注意:这里的getObject调用时机是,在创建的对象依赖了Mapper对象就会去创建该Mapper对象,此时通过MapperFactoryBean去获取

2.10 sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生

上面的getSqlSession()对应源码是:
在这里插入图片描述

2.11 MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean
2.12 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性

这一步是程序员自己定义一个SqlSessionFactory,例如:

@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper")
@ComponentScan("com.athome.tulin.springmybatis")
public class AppConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws IOException {
        System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………");
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }
}

这里定义的SqlSessionFactory 会赋值于2.10的sqlSessionTemplate

2.13 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象

在这里插入图片描述

2.14 当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程

至此:业务类中的引入Mapper对象就复制成功。

 @Autowired
    private OrderMapper orderMapper;

即:这时候的orderMapper就 是赋值了代理对象的对象是有值 的。

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

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

相关文章

Java基于 SpringBoot+Vue 的高校心理教育辅导系统的研究与实现

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

智能工厂4G无线设备预测维护云端联动的DI、AI、DO混合信号处理单元

在现代工业智能化进程中&#xff0c;一款集成了丰富I/O接口并能与各大云平台无缝对接的智能设备显得尤为重要。比如最近推出的这款创新产品&#xff0c;它集合了8路数字输入通道&#xff0c;涵盖了干湿节点的识别功能&#xff0c;适用于多种开关量信号的读取&#xff1b;同时&a…

B端系统优化:用好卡片式设计,效果立竿见影翻倍。

B端系统中&#xff0c;卡片式设计是一种常见的界面设计风格&#xff0c;它将信息和功能组织成一系列独立的卡片&#xff0c;每个卡片通常包含一个特定的信息块或功能模块。 一、卡片式设计的特点包括&#xff1a; 模块化和可重用性&#xff1a;卡片可以被独立设计和开发&#…

svg 属性详解:填充与边框

svg 属性详解&#xff1a;填充与边框 1 颜色和透明度2 填充规则 fill-rule3 边框样式3.1 stroke-width3.2 stroke-linecap3.3 stroke-linejoin3.4 stroke-dasharray 1 颜色和透明度 图像都有颜色&#xff0c;svg 中可以使用属性 fill 和 stroke 来修改图形的颜色。fill 属性设置…

第十六讲_HarmonyOS应用程序包介绍

HarmonyOS应用程序包介绍 1. 应用程序包概述1.1 多 Module 设计的好处1.2 Module 的类型 2. 应用程序包结构2.1 应用的配置文件2.2 资源目录 3. 应用程序编译后包结构 1. 应用程序包概述 官方推荐基于Stage模型开发HarmonyOS应用程序&#xff0c;一个应用可以包含一个或多个Mo…

计算机基础之微处理器简介

微处理器 微处理器定义 微型计算机的CPU也被称为微处理器&#xff0c;是将运算器、控制器和高速缓存集成在一起的超大规模集成电路芯片&#xff0c;是计算机的核心部件。能完成取指令、执行指令&#xff0c;以及与外界存储器和逻辑部件交换信息等操作。 微处理器发展 CPU从…

研发日记,Matlab/Simulink避坑指南(七)——数据溢出钳位Bug

文章目录 前言 背景介绍 问题描述 分析排查 解决方案 总结归纳 前言 见《研发日记&#xff0c;Matlab/Simulink避坑指南(二)——非对称数据溢出Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指南(三)——向上取整Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑…

【CSS】实现鼠标悬停图片放大的几种方法

1.背景图片放大 使用css设置背景图片大小100%&#xff0c;同时设置位置和过渡效果&#xff0c;然后使用&#xff1a;hover设置当鼠标悬停时修改图片大小&#xff0c;实现悬停放大效果。 <!DOCTYPE html> <html lang"en"> <head><meta charset…

C++大学教程(第九版)7.19 将7.10节vector对象的例子转换成array对象

文章目录 题目代码运行截图 题目 (将7.10节vector 对象的例子转换成array 对象)将图7.26中 vector 对象的例子转换成使用array 对象。请消除任何 vector 对象仅有的特性。 分析&#xff1a; vector对象独有的特性&#xff1a; 1.vector对象长度可变 2.长度不同的vector对象可…

基于springboot校友社交系统源码和论文

校友社交系统提供给用户一个校友社交信息管理的网站&#xff0c;最新的校友社交信息让用户及时了解校友社交动向,完成校友社交的同时,还能通过论坛中心进行互动更方便。本系统采用了B/S体系的结构&#xff0c;使用了java技术以及MYSQL作为后台数据库进行开发。系统主要分为系统…

为什么选择快速应用开发:提高业务响应速度与竞争力的关键

如今&#xff0c;企业想要持续蓬勃发展&#xff0c;就需要具备快速满足客户期望的能力。无论是十几年历史的重要市场占有者推出新的APP&#xff0c;还是在疫情期间从线下转向线上电商营销&#xff0c;企业都需要主动适应市场。随着为客户提供新的服务方式&#xff0c;员工也需要…

以前年度资产价值导入以及汇率自动计算解决方案

文章目录 1 Introduction2 Code3 Summary 1 Introduction In the example we will finish ABLDT function and modify asset value . 2 Code DATA: key TYPE bapi1022_key,generaldata TYPE bapi1022_feglg001,generaldatax TYPE bapi1…

品牌突围|内容营销「共创公式」全面讲解

为什么品牌要扎根小红书&#xff1f;除了种草投放&#xff0c;品牌还能做些什么&#xff1f; 在小红书&#xff0c;迎接消费者共创的时代&#xff0c;激活品牌营销的无限潜能。 拥抱多元 在新机遇中预见未来 2023年&#xff0c;各大社交媒体平台涌现出了许多热点&#xff0c…

keil使用教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据 总结 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重…

jupyter python笔记杂乱

问题产生的原因: 无法执行sess.run()的原因是tensorflow版本不同导致的&#xff0c;tensorflow版本2.0无法兼容版本1.0 解决办法: tf.compat.v1.disable_eager_execution() 确保tf’2能运行tf1的代码 notebok打开指定文件夹 直接解决

代码随想录刷题笔记-Day12

1. 二叉树的递归遍历 144. 二叉树的前序遍历https://leetcode.cn/problems/binary-tree-preorder-traversal/94. 二叉树的中序遍历https://leetcode.cn/problems/binary-tree-inorder-traversal/145. 二叉树的后续遍历https://leetcode.cn/problems/binary-tree-postorder-tra…

鸿蒙:@Link装饰器-父子双向同步

子组件中被Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 需要注意&#xff1a;Link装饰的变量与其父组件中的数据源共享相同的值。Link装饰器不能在Entry装饰的自定义组件中使用。 一、装饰器使…

【word】论文、报告:①插入图表题注,交叉引用②快速插入图表目录③删改后一键更新

【word】①插入图表题注&#xff0c;②删改后一键更新 写在最前面插入题注交叉引用修改插入题注的文字格式快速插入图表目录 插入题注后有删改&#xff0c;实现编号一键更新 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&#xff0c;感谢你…

按配置数据绘制配置型地图marker的icon,自定义marker

一、需求 需要自定义配置数据的marker&#xff0c;其中图片内容要灵活可配置自动生成。此处项目用的百度地图。 效果图&#xff1a; 二、思路 用背景图canvas绘制数字的方式生成icon的图片资源。 再将icon生成对应地图marker。 三、代码 canvasImg.js <!-- * descrip…

【misc | CTF】攻防世界 适合作为桌面

天命&#xff1a;这题还挺繁琐的&#xff0c;知识点还不少 目录 步骤1&#xff1a;图片隐写 步骤2&#xff1a;Winhex查看ascii码 步骤1&#xff1a;图片隐写 拿到这张图片&#xff0c;不可能扔进ps会有多图层&#xff0c;普通图片也就一个图层而已 但居然可以有隐写图片这…