Spring扩展点之Mybatis整合模拟
Spring扩展点之Mybatis整合
- 单独使用MyBaitis
- 模拟整合MyBatis到Spring
单独使用MyBaitis
通过配置文件生成sqlSessionFactory,用sqlSessionFactory开启session。通过session获取到mapper执行对应的sql。
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
String result = userMapper.selectById();sqlSession.commit();
sqlSession.flushStatements();
sqlSession.close();
模拟整合MyBatis到Spring
在Spring中我们可以通过如下方式使用mapper。通过分析我们知道userMapper是MyBatis生成的代理对象。但是在下面代码中的mapper并不是代理对象,并且运行还会报错,因为接口不会扫描成为bean。
public interface UserMapper {@Select("select 'user'")String selectById();
}@Component
public class UserService {@Autowiredprivate UserMapper userMapper;public void test() {System.out.println(userMapper.selectById());}
}@ComponentScan("org.example")
public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);UserService userService = context.getBean(UserService.class);userService.test();}
}
我们可以通过FactoryBean接口模拟代理对象的实现。
@Component
public class XianNuFactoryBean implements FactoryBean {public Object getObject() throws Exception {Object proxyInstance = Proxy.newProxyInstance(XianNuFactoryBean.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 处理代理逻辑return null;}});return proxyInstance;}public Class<?> getObjectType() {return UserMapper.class;}
}
添加上述代码后,代码可以正常运行,可以userMapper可以注入成功。但是还有一个问题,这里的代理对象是写死的,我们不能每个mapper都写一个工厂bean吧。此时,我们可以想到利用spring的BeanDefinition把工厂bean注册到容器中。
public class XianNuFactoryBean implements FactoryBean {public Class clazz;public XianNuFactoryBean(Class clazz) {this.clazz = clazz;}public Object getObject() throws Exception {Object proxyInstance = Proxy.newProxyInstance(XianNuFactoryBean.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 处理代理逻辑return null;}});return proxyInstance;}public Class<?> getObjectType() {return clazz;}
}
工厂bean进行改造,增加构造器指定bean类型。
@Component
public class XianNuBeanDefinitionRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(XianNuFactoryBean.class);AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);beanDefinitionRegistry.registerBeanDefinition("userMapper", beanDefinition);}public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {//...}
}
接下来就顺利成章了,我们想要扫描用户所有的mapper然后自动注册到Spring容器中。利用@Import注解配合ImportBeanDefinitionRegistrar。@Import可以获取到所有类的元数据信息,如其他注解的扫描路径。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface XianNuMapperScan {String value();
}@ComponentScan("org.example")
@XianNuMapperScan("org.example.mapper")
@Import(XianNuImportBeanDefinitionRegistrar.class)
public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);UserService userService = context.getBean(UserService.class);userService.test();}
}public class XianNuImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {// 解析扫描路径Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(XianNuMapperScan.class.getName());String path = (String) annotationAttributes.get("value");System.out.println(path);// 扫描路径下的bean?BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(XianNuFactoryBean.class);AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);beanDefinitionRegistry.registerBeanDefinition("userMapper", beanDefinition);}
}
得到扫描路径后需要扫描路径下的类,我们可以利用spring的扫描器来处理。由于Spring本身的扫描器不处理接口,因此我们需要重新 isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法让他处理接口。此外扫描时还有一个exclude/includeFilters的判断,Spring默认添加component注解进行扫描,因此我们可以在mapper上加注解就能扫描到,此外还有一个方法就是在扫描器添加includeFilter。
public class XianNuBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public XianNuBeanDefinitionScanner(BeanDefinitionRegistry registry) {super(registry);}@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata().isInterface();}
}public class XianNuImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {// 解析扫描路径Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(XianNuMapperScan.class.getName());String path = (String) annotationAttributes.get("value");System.out.println(path);// 扫描路径下的beanXianNuBeanDefinitionScanner scanner = new XianNuBeanDefinitionScanner(beanDefinitionRegistry);scanner.addIncludeFilter(new TypeFilter() {public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {return true;}});scanner.scan(path);// BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(XianNuFactoryBean.class);
// AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
// beanDefinitionRegistry.registerBeanDefinition("userMapper", beanDefinition);}
此时扫描出的mapper的BeanDefinition的class是它本身,并不是代理对象,因此还需要对beanDefinition做一些处理。重写scanner父类的doScan方法。
public class XianNuBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public XianNuBeanDefinitionScanner(BeanDefinitionRegistry registry) {super(registry);}// 修改BeanDefinition的class为工厂类@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());beanDefinition.setBeanClassName(XianNuFactoryBean.class.getName());}return beanDefinitionHolders;}// 让扫描器扫描接口@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata().isInterface();}
此时还有一个问题就,就是mapper的代理逻辑是我们自己写的,不是用的MyBatis的。我们看mybatis的原始用法发现,mapper可以通过sqlSessionFactory得到,而sqlSessionFactory是通过配置文件得到。
public class XianNuFactoryBean implements FactoryBean {public Class clazz;@Autowiredprivate SqlSessionFactory sqlSessionFactory;public XianNuFactoryBean(Class clazz) {this.clazz = clazz;}public Object getObject() throws Exception {
// Object proxyInstance = Proxy.newProxyInstance(XianNuFactoryBean.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// // 处理代理逻辑
// System.out.println(method.getName());
// return null;
// }
// });
// return proxyInstance;sqlSessionFactory.getConfiguration().addMapper(clazz);return sqlSessionFactory.openSession().getMapper(clazz);}public Class<?> getObjectType() {return clazz;}
}@ComponentScan("org.example")
@XianNuMapperScan("org.example.mapper")
@Import(XianNuImportBeanDefinitionRegistrar.class)
@Configuration
public class AppConfig {@Beanpublic SqlSessionFactory getSqlSessionFactory() throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);return sqlSessionFactory;}
}
至此模拟整合完成。
相关文章:
Spring扩展点之Mybatis整合模拟
Spring扩展点之Mybatis整合 单独使用MyBaitis模拟整合MyBatis到Spring 单独使用MyBaitis 通过配置文件生成sqlSessionFactory,用sqlSessionFactory开启session。通过session获取到mapper执行对应的sql。 InputStream inputStream Resources.getResourceAsStream(…...
.NET MVC实现电影票管理
.NET MVC(Model-View-Controller)是微软推出的基于 Model-View-Controller 设计模式的 Web 应用框架,属于 ASP.NET Core 的重要组成部分。其核心目标是通过清晰的分层架构实现 高内聚、低耦合 的开发模式,适用于构建可扩展的企业级…...
自媒体账号管理工具:创作罐头使用指南
创作罐头使用指南 1. 关于创作罐头 创作罐头是免费的一站式自媒体运营工具,支持各大自媒体平台多账号管理、全网爆文库、原创检测、视频一键分发、团队管理、各平台数据分析等功能。 2. 安装与注册 2.1. 如何安装创作罐头 从我们的官网下载并安装软件 www.czgts.…...
基于数据可视化+SpringBoot+安卓端的数字化OA公司管理平台设计和实现
博主介绍:硕士研究生,专注于信息化技术领域开发与管理,会使用java、标准c/c等开发语言,以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年,拥有近12年的管理工作经验,拥有较丰富的技术架…...
VSCode离线安装插件
最近在其他电脑设备上部署vscode环境出现问题,在vscode里直接安装插件失败,软件提示如下:(此前已经用此方法安装过中文插件) 这里我们选择手动下载,会自动在浏览器中跳转到该插件的下载链接并自动下载插件&…...
基于Hadoop的汽车大数据分析系统设计与实现【爬虫、数据预处理、MapReduce、echarts、Flask】
文章目录 有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主 项目介绍爬虫数据概览HIve表设计Cars Database Tables 1. cars_data2. annual_sales_volume3. brand_sales_volume4. city_sales_volume5. sales_volume_by_year_and_brand6. sales_distri…...
SHELL32!Shell_MergeMenus函数分析
SHELL32!Shell_MergeMenus函数分析 UINT Shell_MergeMenus( [in] HMENU hmDst, [in] HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags ); 参数 [in] hmDst 类型: HMENU 要向其添加 hmSrc…...
华为云deepseek大模型平台:deepseek满血版
华为云硅基流动使用Chatbox接入DeepSeek-R1满血版671B 1、注册: 华为云deepseek大模型平台注册:https://cloud.siliconflow.cn/i/aDmz6aVN 说明:填写邀请码的话邀请和被邀请的账号都会获得2000 万 Tokens;2个帐号间不会与其他关联…...
AutoGen 技术博客系列 八:深入剖析 Swarm—— 智能体协作的新范式
本系列博文在掘金同步发布, 更多优质文章,请关注本人掘金账号: 人肉推土机的掘金账号 AutoGen系列一:基础介绍与入门教程 AutoGen系列二:深入自定义智能体 AutoGen系列三:内置智能体的应用与实战 AutoGen系列四&am…...
从零开始开发纯血鸿蒙应用之网页浏览
从零开始开发纯血鸿蒙应用 〇、前言一、优化菜单交互1、BuilderFunction.ets2、改造 PageTitleBar 二、网址打开1、方式选择1、使用浏览器打开2、内部打开2.1、声明权限2.2、封装 WebViewPage2.2.1、组件字段2.2.2、aboutToAppear2.2.3、onBackPress2.2.4、标题栏2.2.4、网页内…...
【大模型LLM】DeepSeek LLM Scaling Open-Source Language Models with Longtermism
深度探索LLM:以长期主义扩展开源语言模型 0.论文摘要 开源大语言模型(LLMs)的快速发展确实令人瞩目。然而,以往文献中描述的扩展规律得出了不同的结论,这为LLMs的扩展蒙上了一层阴影。我们深入研究了扩展规律&#…...
分布式事务-本地消息表学习与落地方案
本文参考: 数据库事务系列04-本地消息表实现分布式事务 基础概念 本地消息表实现分布式事务最终一致性的核心:是通过上游本地事务的原子性持久性,配合中间件的重试机制,从而实现调用下游的最终一致性。 这里有几个要点可以解析一…...
Debezium系列之:记录一次源头数据库刷数据,造成数据丢失的原因
Debezium系列之:记录一次源头数据库刷数据,造成数据丢失的原因 一、背景二、查看topic日志信息三、结论四、解决方法一、背景 源头数据库在很短的时间内刷了大量的数据,部分数据在hdfs丢失了 理论上debezium数据采集不会丢失,就需要排查数据链路某个节点是否有数据丢失。 …...
PHP约课健身管理系统小程序源码
🏋️♂️ 约课健身管理系统小程序:重塑健身预约体验,引领数字化健身新时代 一款基于ThinkPHPUniapp框架,由米扬精心雕琢的约课健身管理系统小程序,专为健身房、健身工作室、运动会所、运动场馆、瑜伽馆、拳馆等泛健…...
Java之泛型
文章目录 首先接着上一篇(集合)文章,来个非常牛逼的易错题传统集合问题分析泛型快速入门案例泛型介绍泛型的好处泛型的语法泛型的声明泛型的实例化泛型使用举例泛型使用的注意事项和细节 自定义泛型自定义泛型方法 自定义泛型接口自定义泛型方…...
图论 之 最小生成树
文章目录 题目1584.连接所有点的最小费用 最小生成树MST,有两种算法进行求解,分别是Kruskal算法和Prim算法Kruskal算法从边出发,适合用于稀疏图Prim算法从顶点出发,适合用于稠密图:基本思想是从一个起始顶点开始&#…...
STM32-有关内存堆栈、map文件
STM32堆栈空间大小设置_stm32堆栈分配大小-CSDN博客 STM32堆栈的大小及内存四(五)区的分析 - 天街小雨润地狠 - 博客园 .map文件的位置...
Linux系统中常见的词GNU是什么意思?
GNU 是 “GNU’s Not Unix” 的递归缩写,它是一个自由软件项目,旨在创建一个完全自由的操作系统。这个名字反映了GNU项目的核心理念:它试图创建一个类Unix的系统,但不是Unix本身。 GNU 项目由 理查德斯托曼(Richard S…...
【个人开源】——从零开始在高通手机上部署sd(二)
代码:https://github.com/chenjun2hao/qualcomm.ai 推理耗时统计 单位/ms 硬件qnncpu_clipqnncpu_unetqnncpu_vaehtp_cliphtp_unethtp_vae骁龙8 gen124716.994133440.39723.215411.097696.327 1. 下载依赖 下载opencv_x64.tar,提取码: rrbp下载opencv_aarch64.t…...
【MCU驱动开发概述】
MCU驱动开发概述 目录 MCU驱动开发概述二、驱动开发的目的三、驱动开发的关键组成部分四、示例 - LED 控制驱动 一、引言 MCU(Microcontroller Unit),即微控制器单元,是一种集成在单个芯片上的计算机系统,通常用于控制…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
