手写@MapperScan

news2025/1/9 6:32:01
  • 定义一个EnableMapperScan注解
    @Import(MapperProxyHandlerRegister.class) 标注将MapperProxyHandlerRegister导入到容器中。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(MapperProxyHandlerRegister.class)
public @interface EnableMapperScan {

    String baskPackages();

}
  • MapperProxyHandlerRegister

ImportBeanDefinitionRegistrar 是一个特殊组件,实现了这个接口的组件registry会动态给容器中批量注册组件,这里的关键就是获取到baskPackages 指定的路径,然后批量扫描classpath下的所有的接口,然后定义BeanDefinition,注册到容器中。
注意:AnnotationMetadata metadata封装的是标注了@Import注解的那个类。这样就可以拿到最终因哪个类扫描的这个配置类。

/**
 * Mapper组件注册器
 */
public class MapperProxyHandlerRegister implements ImportBeanDefinitionRegistrar {

    /**
     * @param metadata  注意:metadata是Spring解析@Import注解标注的那个类的元信息,而不是导入的配置类的元信息
     * @param registry  BeanDefinition注册器
     * @param generator BeanName生成器
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
                                        BeanDefinitionRegistry registry,
                                        BeanNameGenerator generator) {
        //这里的这个metadata封装的是标注了@Import注解的那个类,如果标注@Import注解因其他类似@Enablexxx注解进一步解析到的
        //那这里的metadata封装的就是解析@Enablexxx那个类的元数据信息,
        //总之,metadata封装的就是Spring原始解析类上关联的@Import注解的类
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(EnableMapperScan.class.getName());
        String baskPackages = annotationAttributes.get("baskPackages").toString();
        //获取此包下所有的类
        try {
            Set<Class<?>> scan = scan(baskPackages);
            if (CollUtil.isNotEmpty(scan)){
                //遍历接口
                for (Class<?> mapperClass : scan) {
                    //注册到容器
                    RootBeanDefinition definition = new RootBeanDefinition();
                    definition.setBeanClass(MapperFactoryBean.class);
                    //设置构造方法参数值
                    ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
                    constructorArgumentValues.addIndexedArgumentValue(0,mapperClass);
                    definition.setConstructorArgumentValues(constructorArgumentValues);
                    //definition.setFactoryMethodName("getObject");
                    //注册Bean
                    registry.registerBeanDefinition(mapperClass.getName(),definition);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /*
        1. 创建A   --> 依赖B --> 创建B(依赖A) -> 为A创建代理对象 ,并从缓存中拿,后注入A的是一个代理对象。


    * */
    public static Set<Class<?>> scan(String basePackage) throws IOException, ClassNotFoundException {
        // 创建一个扫描器
        //ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        // 可以添加过滤器来筛选特定类型的类,这里先不添加任何过滤器,获取所有类
        // 例如,如果只想获取带有特定注解的类,可以添加如下代码
        // scanner.addIncludeFilter(new AnnotationTypeFilter(YourAnnotation.class));
        // 如果只想获取特定接口的实现类,可以添加如下代码
        // scanner.addIncludeFilter(new AssignableTypeFilter(YourInterface.class));
        // 构建要扫描的资源路径模式
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + "/**/*.class";
        Resource[] resources = resolver.getResources(packageSearchPath);
        MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        Set<Class<?>> classes = new HashSet<>();
        for (Resource resource : resources) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            String sourceClassName = metadataReader.getClassMetadata().getClassName();
            if (metadataReader.getClassMetadata().isInterface()){
                //只扫描mapper接口
                classes.add(Class.forName(sourceClassName));
            }
            //System.out.println("sourceClassName = " + sourceClassName);
            // 排除接口和抽象类,只获取具体类
            //if (!metadataReader.getClassMetadata().isInterface() &&!metadataReader.getClassMetadata().isAbstract()) {
            //    String className = metadataReader.getClassMetadata().getClassName();
            //    classes.add(Class.forName(className));
            //}
        }
        return classes;
    }

    private static String resolveBasePackage(String basePackage) {
        return basePackage.replace('.', '/');
    }

  • MapperProxy

这个类封装了代理对象的核心方法,主要就是获取mapper接口上标注了@Select @Update @Delete注解的方法,解析sql参数,获取sqlsessionfactory,然后执行sql。这里简单模拟输出获取到的sql。

/**
    mapper组件动态代理方法拦截实现
 */
public class MapperProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class){
            return method.invoke(this,args);
        }
        String name = method.getDeclaringClass().getName();
        String methodName = method.getName();
        System.out.println("mapperProxy 执行方法,被代理对象拦截 ===》 " + name + "." +methodName + "()");
        Select select = method.getAnnotation(Select.class);
        if (select != null){
            String[] value = select.value();
            //拦截方法
            System.out.println("拦截到的SQL:===> " + Arrays.toString(value));
        }

        Class<?> returnType = method.getReturnType();
        if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()){
            return 1;
        }
        return null;
    }
}
  • MapperFactoryBean

这个类实现了FactoryBean 接口,实现了这个接口的类Spring会进一步调用getObject方法创建Bean对象,这个Bean对象的类型是getObjectType返回的数据类型,由此,这个类型就是真正的Mapper接口类型,可以实现基于Mapper接口的自动装配。

public class MapperFactoryBean implements FactoryBean {

    private Class mapperInterFaceClazz;

    public MapperFactoryBean(Class mapperInterFaceClazz){
        this.mapperInterFaceClazz = mapperInterFaceClazz;
    }

    @Override
    public Object getObject() throws Exception {
        /**
            在创建代理对象时,传入需要实现的接口mapperInterFaceClazz
            如果这个接口还继承了其他接口,在基于实现类调方法时,父接口的方法也会被代理对象拦截。
            原理很简单,面向对象三大特征之一: 继承,子类继承父类,就把父类所有的内容全都继承
            JVM在判断时,如果满足继承结构以及安全检查
         */
        return Proxy.newProxyInstance(mapperInterFaceClazz.getClassLoader(),new Class[]{mapperInterFaceClazz},new MapperProxy());
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterFaceClazz;
    }

}
  • MainApp
@SpringBootApplication
@EnableMapperScan(baskPackages = "com.example.ssm3.mapper")

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

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

相关文章

365天深度学习训练营:第N2周:构建词典

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 本周任务: 使用N1周的.txt 文件构建词典&#xff0c;停用词请自定义 1. 导入数据 from torchtext.vocab import build_vocab_from_iterator from collection…

vue2迁移至rsbuild

背景 由于远程机器配置较低&#xff0c;每次运行vue2项目都会非常卡。后期项目文件、路由更多的时候&#xff0c;启动到一半直接会跳出open too many files类似的错误&#xff0c;尝试将路由屏蔽掉只剩下开发所需的一个路由也不行&#xff08;不是说webpack的打包是全部打包&am…

升级 Spring Boot 3 配置讲解 — 新版本的秒杀系统怎么做?

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; 1. Spring Boot 3 升级指南 在升级 Spring Boot 3 之前&#xff0c;首先需要确保你的项目已经升级到 Java 17&#xff0c;因为 Spring Boot 3 不再支持 Java 8 和 Java 11。接下来&…

Seata的部署与微服务集成

文章目录 Seata的部署与微服务集成1. Seata介绍2. 部署TC服务2.1 数据准备2.2 配置文件2.3 docker 部署2.4 访问 3. 微服务集成Seata3.1 引入服务3.2 改造配置3.3 添加数据库表3.4 注解标记 Seata的部署与微服务集成 1. Seata介绍 Seata 是一款开源的分布式事务解决方案&…

NFS 组件容器化部署实战指南

文章目录 前言部署NFS服务器K8S部署NFS问题记录 前言 使用nfs-client-provisioner这个应用&#xff0c;利用nfs server给kubernets提供作为持久化后端&#xff0c;并且动态提供pv。所有节点需要安装nfs-utils组件&#xff0c;并且nfs服务器与kubernets worker节点都能网络连通…

【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050

1. MPU6050简介 MPU6050是一个6轴姿态传感器&#xff0c;可以测量芯片自身X、Y、Z轴的加速度、角速度参数&#xff0c;通过数据融合&#xff0c;可进一步得到姿态角&#xff0c;常应用于平衡车、飞行器等需要检测自身姿态的场景3轴加速度计&#xff08;Accelerometer&#xff…

裸机器搭建k8s部署 1.28.10版本

问了搭建k8s集群踩了很多坑&#xff0c;问题主要出现在网络插件处&#xff0c;因此主要是master节点操作问题。重新走一下流程整理一下笔记。 目录 虚拟机准备 虚拟机 系统版本信息 修改镜像地址 配置静态ip 关闭防火前和交换分区 转发 IPv4 并让 iptables 看到桥接流量…

HCIE-day10-ISIS

ISIS ISIS&#xff08;Intermediate System-to-Intermediate System&#xff09;中间系统到中间系统&#xff0c;属于IGP&#xff08;内部网关协议&#xff09;&#xff1b;是一种链路状态协议&#xff0c;使用最短路径优先SPF算法进行路由计算&#xff0c;与ospf协议有很多相…

70.爬楼梯 python

爬楼梯 题目题目描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a; 题解思路分析Python 实现代码空间优化代码解释提交结果 题目 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff…

优质内容在个人IP运营中的重要性:以开源AI智能名片商城小程序为应用实例的深度探讨

摘要&#xff1a;在数字化时代&#xff0c;个人品牌&#xff08;IP&#xff09;的塑造与传播已成为各行各业提升影响力、吸引用户关注、促进商业转化的关键策略。优质内容作为连接个人IP与目标受众的桥梁&#xff0c;其在个人IP运营中的重要性不言而喻。本文旨在深入探讨优质内…

有限元分析学习——Anasys Workbanch第一阶段笔记(8)水杯案例的对称与轴对称处理

目录 1 序言 2 对称处理 2.1 模型处理 2.2 网格划分、约束载荷及接触设置 2.3 计算结果 3 轴对称处理 3.1 对称与轴对称概念 3.2 轴对称问题的应用 3.2.1 创建分析案例 3.2.2 导入并处理模型 3.2.3 网格划分、约束载荷及接触设置 3.2.4 后处理计算结果 1 序言 本章…

网络安全-web渗透环境搭建-BWAPP(基础篇)

01--所需系统环境&#xff1a; 虚拟主机系统部署&#xff08;vmware&#xff0c;虚拟主机创建、虚拟主机网络配置&#xff08;桥接&#xff0c;便于网络中多个主机都能访问虚拟主机&#xff09;、虚拟软件功能&#xff0c;快照、克隆、镜像文件加载&#xff0c;ova文件制作&am…

Java 实现 Elasticsearch 查询当前索引全部数据

Java 实现 Elasticsearch 查询当前索引全部数据 需求背景通常情况Java 实现查询 Elasticsearch 全部数据写在最后 需求背景 通常情况下&#xff0c;Elasticsearch 为了提高查询效率&#xff0c;对于不指定分页查询条数的查询语句&#xff0c;默认会返回10条数据。那么这就会有…

算能AI计算服务器SE5设备树的二次修改实操

目录 1.大纲 2.实操 2.下载对应文件包 3.解包启动文件 4.修改对应的设备树 5.重启后 教程链接&#xff1a;https://github.com/sophgo/sophon-tools/tree/main/source/pmemory_edit 1.大纲 2.实操 2.1 选择串口&#xff0c;波特率115200&#xff0c;重启设备&#xff0…

Python的Matplotlib库应用(超详细教程)

目录 一、环境搭建 1.1 配置matplotlib库 1.2 配置seaborn库 1.3 配置Skimage库 二、二维图像 2.1 曲线&#xff08;直线&#xff09;可视化 2.2 曲线&#xff08;虚线&#xff09;可视化 2.3 直方图 2.4 阶梯图 三、三维图像 3.1 3D曲面图 3.2 3D散点图 3.3 3D散…

Flutter:封装一个自用的bottom_picker选择器

效果图&#xff1a;单列选择器 使用bottom_picker: ^2.9.0实现&#xff0c;单列选择器&#xff0c;官方文档 pubspec.yaml # 底部选择 bottom_picker: ^2.9.0picker_utils.dart AppTheme&#xff1a;自定义的颜色 TextWidget.body Text() <Widget>[].toRow Row()下边代…

牛客网刷题 ——C语言初阶(6指针)——BC106 上三角矩阵判定

1. 题目描述——BC106 上三角矩阵判定 牛客网OJ题链接 描述 KiKi想知道一个n阶方矩是否为上三角矩阵&#xff0c;请帮他编程判定。上三角矩阵即主对角线以下的元素都为0的矩阵&#xff0c;主对角线为从矩阵的左上角至右下角的连线。 示例 输入&#xff1a; 3 1 2 3 0 4 5 0 0…

力扣刷题:数组OJ篇(下)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 目录 1.轮转数组&#xff08;1&#xff09;题目描述…

《(限)战斗天赋VR》V02122024官方中文学习版

《(限)战斗天赋VR》官方中文版https://pan.xunlei.com/s/VODaeHDXSxw4BNDNl39dxJXnA1?pwdusm5# 一款具有挑战性的基于物理的roguelite剑术格斗游戏&#xff0c;你可以在一个超级无缝的程序地牢中创造自己的战斗风格&#xff0c;体验无与伦比的游戏体验。有80多种敌人变种、10…

《Spring Framework实战》3:概览

欢迎观看《Spring Framework实战》视频教程 Spring Framework 为基于现代 Java 的企业应用程序提供了全面的编程和配置模型 - 在任何类型的部署平台上。 Spring 的一个关键要素是应用程序级别的基础设施支持&#xff1a;Spring 专注于企业应用程序的 “管道”&#xff0c;以便…