spring整合mybatis的底层原理

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 {@Beanpublic 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@Autowiredprivate UserMapper userMapper;@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate 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();}@Overridepublic Object getObject() throws Exception {//动态代理获取UserMapper接口对象System.out.println("6.……getObject……");return sqlSession.getMapper(mapperInterface);}@Overridepublic Class<?> getObjectType() {return mapperInterface;}
}
1.7 自定义Bean注册类
public class CondorHeroImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic 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() {@Overridepublic 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);}@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)  {//只关心接口(判断是否是接口)return beanDefinition.getMetadata().isInterface();}/*** 扫描路径并得到beanDefinition* @param basePackages* @return*/@Overrideprotected 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(在类ClassPathMapperScanner的方法processBeanDefinitions中)
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> {}
 @Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}@Overridepublic 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 {@Beanpublic 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对象就复制成功。

 @Autowiredprivate OrderMapper orderMapper;

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据开发之离线数仓项目(用户行为采集平台)(可面试使用)

第 1 章&#xff1a;数据仓库概念 数据仓库&#xff0c;是为企业指定决策&#xff0c;提供数据支持的&#xff0c;可以帮助企业&#xff0c;改进业务流程、提高产品质量等。 数据仓库的输入数据通常包括&#xff1a;业务数据、用户行为数据和爬虫数据等。 业务数据&#xff1a…

HiveSQL题——数据炸裂和数据合并

目录 一、数据炸裂 0 问题描述 1 数据准备 2 数据分析 3 小结 二、数据合并 0 问题描述 1 数据准备 2 数据分析 3 小结 一、数据炸裂 0 问题描述 如何将字符串1-5,16,11-13,9" 扩展成 "1,2,3,4,5,16,11,12,13,9" 且顺序不变。 1 数据准备 with da…

【原创】VMware创建子网,并使用软路由获得访问互联网的能力,并通过静态路由让上层网络访问位于虚拟机的子网

前言 一看标题就很离谱&#xff0c;确实内容也有点复杂&#xff0c;我的初衷是为后面搞软路由做准备&#xff0c;先通过VMware进行可行性验证&#xff0c;确定方案是否可行&#xff0c;再做下一步的计划。结论当然可以的&#xff0c;能通能访问&#xff0c;强的不行。 网络拓…

2023强网杯复现

强网先锋 SpeedUp 要求2的27次方的阶乘的逐位之和 在A244060 - OEIS 然后我们将4495662081进行sha256加密 就得到了flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} MISC easyfuzz 通过尝试输入字符串判断该程序对输入字符的验证规则为9…

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于OOA-LSSVM鱼鹰算法优化最小…

Unix/Linux上的五种IO模型

a.阻塞 blocking 调用者调用了某个函数&#xff0c;等待这个函数返回&#xff0c;期间什么也不做&#xff0c;不停的去检查这个函数有没有返回&#xff0c;必须等这个函数返回才能进行下一步动作。 注意&#xff1a;阻塞并不是函数的行为&#xff0c;而是跟文件描述符有关。通…

HTM标签 - 2

HTM标签 超链接标签 超链接标签&#xff1a;<a> 文本或图片 </a> 用法1&#xff1a;在页面中使用超链接标签跳转到另一个页面 属性描述href页面跳转的地址&#xff0c;相对地址或绝对地址&#xff1b;###&#xff1a;空连接&#xff1b;#&#xff1a;跳转到当前…

树和二叉树基础

树和二叉树基础 1.1树的概念 树是在数据结构中第一次接触到的非线性结构。 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它 叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&am…

LVS常用的NAT模式和DR模式实战示例

引言&#xff1a;紧接上文&#xff0c;了解LVS&#xff0c;这一篇就够了-CSDN博客&#xff0c;今天我们对LVS常用的两种模式来进行示例配置演示 LVS-NAT模式 1、环境准备 准备 3 台纯净的虚拟机 关闭防火墙和selinux 例&#xff1a; lvs-server 添加两个网卡 NAT模式 …

如何在Excel中清除单元格的格式?这里有详细步骤

Microsoft Excel提供了大量样式选项来自定义电子表格的外观。但是&#xff0c;如果你需要删除格式&#xff0c;则可以很容易地删除选定单元格和整个工作表的格式。我们将向你展示如何操作。 ​注意&#xff1a;清除格式只会删除文本的样式&#xff1b;将保留你的实际文本。 如…

C++类和对象——友元详解

目录 1.友元是什么 2.全局函数做友元 3.友元类 类外写成员函数 4.成员函数做友元 1.友元是什么 在生活中&#xff0c;我们的家里有客厅也有卧室 客人都能进出你家的客厅&#xff0c;但不能进入你家的卧室 但只要你允许&#xff0c;你也可以让自己的好朋友进入你的卧…

Qt扩展-muParser数学公式解析

muParser数学公式解析 一、概述1. 针对速度进行了优化2. 支持的运算符3. 支持的函数4. 用户定义的常量5. 用户定义的变量6. 自定义值识别回调7. 其他功能 二、内置函数三、内置二元运算符四、三元运算符五、内置常量六、源码引入1. 源码文件2. 编译器开关1. MUP_BASETYPE2.MUP_…