MyBatis 反射模块

news2025/2/24 10:45:29

文章目录

  • 前言
  • 反射模块实现
    • Reflector
    • ReflectorFactory
    • Invoker
    • MetaClass
    • MetaObject
  • 反射模块应用
    • SqlSessionFactory
    • 执行SQL

前言

MyBatis在进行参数处理、结果集映射等操作时会使用到大量的反射操作,Java中的反射功能虽然强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射模块,该模块位于org.apache.ibatis.reflection包下,它对常见的反射操作做了进一步的封装,提供了更加简洁方便的反射API。

反射模块实现

Reflector

Reflector是反射模块的基础,每个Reflector对象都对应一个类,在Reflector中缓存了反射需要使用的类的元信息

Reflector中提供的相关属性的含义

  // 对应的Class 类型 
  private final Class<?> type;
  // 可读属性的名称集合 可读属性就是存在 getter方法的属性,初始值为null
  private final String[] readablePropertyNames;
  // 可写属性的名称集合 可写属性就是存在 setter方法的属性,初始值为null
  private final String[] writablePropertyNames;
  // 记录了属性相应的setter方法,key是属性名称,value是Invoker方法
  // 他是对setter方法对应Method对象的封装
  private final Map<String, Invoker> setMethods = new HashMap<>();
  // 属性相应的getter方法
  private final Map<String, Invoker> getMethods = new HashMap<>();
  // 记录了相应setter方法的参数类型,key是属性名称 value是setter方法的参数类型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  // 和上面的对应
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  // 记录了默认的构造方法
  private Constructor<?> defaultConstructor;

  // 记录了所有属性名称的集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

在Reflector的构造器中会完成相关的属性的初始化操作

  // 解析指定的Class类型 并填充上述的集合信息
  public Reflector(Class<?> clazz) {
    type = clazz; // 初始化 type字段
    addDefaultConstructor(clazz);// 设置默认的构造方法
    addGetMethods(clazz);// 获取getter方法
    addSetMethods(clazz); // 获取setter方法
    addFields(clazz); // 处理没有getter/setter方法的字段
    // 初始化 可读属性名称集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    // 初始化 可写属性名称集合
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    // caseInsensitivePropertyMap记录了所有的可读和可写属性的名称 也就是记录了所有的属性名称
    for (String propName : readablePropertyNames) {
      // 属性名称转大写
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      // 属性名称转大写
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

反射也可以在项目中我们直接拿来使用,定义一个普通的Bean对象。

/**
 * 反射工具箱
 *    测试用例
 */
public class Person {

    private Integer id;

    private String name;

    public Person(Integer id) {
        this.id = id;
    }

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
}

Reflector中提供的公共的API方法

方法名称作用
getType获取Reflector表示的Class
getDefaultConstructor获取默认的构造器
hasDefaultConstructor判断是否有默认的构造器
getSetInvoker根据属性名称获取对应的Invoker 对象
getGetInvoker根据属性名称获取对应的Invoker对象
getSetterType获取属性对应的类型 比如:
String name; // getSetterType(“name”) --> java.lang.String
getGetterType与上面是对应的
getGetablePropertyNames获取所有的可读属性名称的集合
getSetablePropertyNames获取所有的可写属性名称的集合
hasSetter判断是否具有某个可写的属性
hasGetter判断是否具有某个可读的属性
findPropertyName根据名称查找属性

ReflectorFactory

MyBatis提供了一个ReflectorFactory工厂对象,用来获取Reflector对象。

ReflectorFactory接口的定义

public interface ReflectorFactory {
  // 检测该ReflectorFactory是否缓存了Reflector对象
  boolean isClassCacheEnabled();
  // 设置是否缓存Reflector对象
  void setClassCacheEnabled(boolean classCacheEnabled);
  // 创建指定了Class的Reflector对象
  Reflector findForClass(Class<?> type);
}

具体实现

MyBatis只为该接口提供了DefaultReflectorFactory这一个实现类。他与Reflector的关系如下:

image.png

DefaultReflectorFactory中的实现,代码比较简单

public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  // 实现对 Reflector 对象的缓存
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {// 开启缓存
      // synchronized (type) removed see issue #461
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      // 没有开启缓存就直接创建
      return new Reflector(type);
    }
  }
}

通过上面的介绍,我们可以具体的来使用下,加深对其的理解。

package com.domain;

public class Student {
  
    public Integer getId() {
        return 6;
    }

    public void setId(Integer id) {
        System.out.println(id);
    }

    public String getUserName() {
        return "张三";
    }
}

这个Bean我们做了简单的处理

    @Test
    public void test02() throws Exception{
        ReflectorFactory factory = new DefaultReflectorFactory();
        Reflector reflector = factory.findForClass(Student.class);
        System.out.println("可读属性:"+Arrays.toString(reflector.getGetablePropertyNames()));
        System.out.println("可写属性:"+Arrays.toString(reflector.getSetablePropertyNames()));
        System.out.println("是否具有默认的构造器:" + reflector.hasDefaultConstructor());
        System.out.println("Reflector对应的Class:" + reflector.getType());
    }

Invoker

针对于Class中Field和Method的调用,在MyBatis中封装了Invoker对象来统一处理(有使用到适配器模式)

/**
 * @author Clinton Begin
 */
public interface Invoker {
  // 执行Field或者Method
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  // 返回属性相应的类型
  Class<?> getType();
}

该接口有对应的三个实现

image.png

通过上面的案例查看使用效果

package com.domain;

public class Student {


    public Integer getId() {
        System.out.println("读取id");
        return 6;
    }

    public void setId(Integer id) {
        System.out.println("写入id:"+id);
    }

    public String getUserName() {

        return "张三";
    }
}

测试代码

    public void test03() throws Exception{
        ReflectorFactory factory = new DefaultReflectorFactory();
        Reflector reflector = factory.findForClass(Student.class);
        // 获取构造器 生成对应的对象
        Object o = reflector.getDefaultConstructor().newInstance();
        MethodInvoker invoker1 = (MethodInvoker) reflector.getSetInvoker("id");
        invoker1.invoke(o,new Object[]{999});
        // 读取
        Invoker invoker2 = reflector.getGetInvoker("id");
        invoker2.invoke(o,null);
    }

MetaClass

在Reflector中可以针对普通的属性操作,但是如果出现了比较复杂的属性,比如 private Person person; 这种,我们要查找的表达式 person.userName,针对这种表达式的处理,可以通过MetaClass来处理,通过 Reflector 和 ReflectorFactory 的组合使用实现对复杂的属性表达式的解析。


public class MetaClass {
  // 缓存 Reflector
  private final ReflectorFactory reflectorFactory;
  // 创建 MetaClass时 会指定一个Class reflector会记录该类的相关信息
  private final Reflector reflector;

  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }
  // ....
}

准备Bean对象


public class RichType {

    private RichType richType;

    private Map richMap = new HashMap();

    private List richList = new ArrayList() {
        {
            add("bar");
        }
    };

    public RichType getRichType() {
        return richType;
    }

    public void setRichType(RichType richType) {
        this.richType = richType;
    }

    public List getRichList() {
        return richList;
    }

    public void setRichList(List richList) {
        this.richList = richList;
    }

    public Map getRichMap() {
        return richMap;
    }

    public void setRichMap(Map richMap) {
        this.richMap = richMap;
    }
}

测试代码

    @Test
    public void test7(){
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
        System.out.println(meta.hasGetter("richList"));
        System.out.println(meta.hasGetter("richMap"));
        System.out.println(meta.hasGetter("richList[0]"));

        System.out.println(meta.hasGetter("richType"));
        System.out.println(meta.hasGetter("richType.richList"));
        System.out.println(meta.hasGetter("richType.richMap"));
        System.out.println(meta.hasGetter("richType.richList[0]"));

        System.out.println(Arrays.toString(meta.getGetterNames()));
    }

MetaObject

通过MetaObject对象解析复杂的表达式来对提供的对象进行操作。

    @Test
    public void shouldGetAndSetField() {
        RichType rich = new RichType();
        MetaObject meta = SystemMetaObject.forObject(rich);
        meta.setValue("richField", "foo");
        System.out.println(meta.getValue("richField"));
    }

反射模块应用

反射模块在MyBatis的核心处理层中的实际应用

SqlSessionFactory

在创建SqlSessionFactory操作的时候会完成Configuration对象的创建,而在Configuration中默认定义的ReflectorFactory的实现就是DefaultReflectorFactory对象

image.png

在解析全局配置文件的代码中,给用户提供了ReflectorFactory的扩展,也就是我们在全局配置文件中可以通过reflectorFactory标签来使用我们自定义的ReflectorFactory

执行SQL

在Statement获取结果集后,在做结果集映射的使用有使用到,在DefaultResultSetHandler的createResultObject方法中。

image.png

在DefaultResultSetHandler的getRowValue方法中在做自动映射的时候,createAutomaticMappings对其应用。

image.png

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

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

相关文章

MSTP+VRRP vlan接口作为网关(2)

SW2 g0/0/2 g0/0/5 g0/0/3 g0/0/4 shutdow 链路失效, vlan 3 的 根桥、master 依然是sw2 PC3的数据包会什么还会到达外部环回口&#xff1f; SW2: dis stp instance 2 brief dis vrrp brief vlan3的主机PC3访问3.3.3.3.数据包发给网关(master)Sw2 pc3 : tracert …

go语言unsafe.Pointer与uintptr

以下内容来源go语言圣经 1、unsafe.Pointer&#xff0c;相当于c语言中的void *类型的指针&#xff0c;如果需要运算需要转成uintptr类型的指针 2. uintptr uintptr是一个无符号的整型&#xff0c;它可以保存一个指针地址。 它可以进行指针运算。 uintptr无法持有对象, GC不把…

新能源汽车运行安全性能检验规程需要哪些CAN数据才符合标准

新能源汽车的前生命周期包括了整车制造、使用、转让市场及报废回收这几个主要阶段&#xff0c;在政策大力扶持下&#xff0c;国内新能源汽车的制造产业链完善&#xff0c;补贴培育市场取得丰硕的果实。目前来说&#xff0c;我国新能源汽车有着技术领先、设计先进、低成本优势&a…

Android集成微信支付

​ 打开微信开放平台登录账户后点击创建应用 根据提示填写完相应的内容提交审核通过后&#xff0c;需要开通支付权限。 1.接着在你的项目工程build.gradle文件中添加微信支付依赖库 ​api com.tencent.mm.opensdk:wechat-sdk-android: 2.在你的包的根目录下&#xff0c;创建w…

Nodejs+vue体育用品商城商品购物推荐系统_t81xg

本课题基于协同过滤算法&#xff0c;主要采用nodejs技术和MySQL数据库技术以及vue框架进行开发。功能主要包括首页、个人中心、用户管理、商品分类管理、商品信息管理、交流论坛、留言板、系统管理、订单管理等功能&#xff0c;从而实现个性化智能体育商品推荐方式&#xff0c;…

MyBatis基础之动态SQL

文章目录 动态 SQLif 元素choose-when-otherwise 元素where 元素set 元素foreach 元素 动态 SQL 简而言之&#xff0c;动态 SQL 就是在 Mapper 中使用分支、循环等逻辑。常见的动态 SQL 元素包括&#xff1a; if 元素choose-when-otherwise 元素where 元素set 元素foreach 元…

2024年中国计量大学MBA最新招生计划公布:有哪些看点?

中国计量大学MBA项目立项于2023年&#xff0c;第一年招生就顺利完成开班任务&#xff0c;虽然人数不多&#xff0c;但是因为其有自身的项目培养定位&#xff0c;因此未来的市场中还是可以保持自身的优势。从2024年最新的招生计划来看&#xff0c;中国计量大学的总体计划依然采取…

大数据-离线项目

第一章 需求分析 需求分析与设计 项目需求背景 "某APP上线后 经营得当 使用户 日活量增多 出现以下问题""营销分析断层&#xff1a;"市场营销成本居高不下&#xff0c;投放拉新的效果追踪出现断层&#xff0c;无法追踪各渠道实际转化率&#xff0c;难以…

应用在摄像头对焦镜头中的马达驱动芯片

摄像头&#xff08;CAMERA或WEBCAM&#xff09;又称为电脑相机、电脑眼、电子眼等&#xff0c;是一种视频输入设备&#xff0c;被广泛的运用于视频会议、远程医疗及实时监控等方面。普通的人也可以彼此通过摄像头在网络进行有影像、有声音的交谈和沟通。另外&#xff0c;人们还…

记录uniapp 微信小程序胶囊分享bug处理 (第一次点击分享的时候不能修改标题或者路径)

问题复现情况: 以下为博主代码&#xff1a; 问题原因: 因为博主的业务需求&#xff0c;需要在分享的时候调用后端的接口来实现分享挂载推广关系。也就是因为调用了接口导致 重新给分享赋值标题或者路径的时候赋值不上。&#xff08;刚开始怀疑是微信分享的问题&#xff0c;后面…

HarmonyOS创作激励计划启动:助力技术创作突破边界

即日起推出HarmonyOS创作激励计划&#xff0c;成功投稿并入选的文章将在HarmonyOS开发者公众号上线&#xff0c;9大技术社区同步宣发&#xff0c;不仅有丰厚稿酬&#xff0c;还有机会赢取创作奖品&#xff01; 活动时间 即日起-2024年12月31日&#xff0c;每季度按照活动规则评…

RabbitMQ 几种模式

一、Hello World 模式 在这一部分中&#xff0c;我们将用 Java 编写两个程序。发送单个消息的生产者和接收消息并打印出来的消费者。模型如下所示&#xff1a; 在下图中&#xff0c;“ P” 是我们的生产者&#xff0c;“ C” 是我们的消费者。中间的框是一个队列 RabbitMQ 代表…

灞桥论“健” 共话康养 灞桥康养论坛取得圆满成功

随着我国“老龄化”的加速&#xff0c;养老资源的匮乏已经成为一个十分严峻的社会问题。同时随着生活水平的大幅提高&#xff0c;康养产业应势而生。涵盖了养老、医疗、体育、养生、旅游等多个领域的康养产业&#xff0c;不仅要为老人们实现“老有所乐、身体健康”的理想&#…

【数据结构】二叉树的前序遍历(七)

题目&#xff1a;二叉树的前序遍历 题目详情&#xff1a;给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历&#xff1b; 我们先来看几个示例&#xff1a; 输入&#xff1a;root [ 1&#xff0c;null&#xff0c;2&#xff0c;3 ] 输出&#xff1a;[ 1&#xf…

【送书】从不了解用户画像,到用画像数据赋能业务看这一本书就够了丨《用户画像:平台构建与业务实践》

文章目录 内容了解本书目录参与方式 &#x1f308;hello&#xff01; 各位铁汁们大家好啊&#xff0c;今天给大家推荐的的是机械工业出版社的 《用户画像&#xff1a;平台构建与业务实践》这本书&#xff01;   ⛳️大数据时代&#xff0c;如何有效地挖掘数据价值并通过画像数…

jvm-sandbox-repeater时间mock插件设计与实现

一、背景 jvm-sandbox-repeater实现了基础的录制回放流程编排&#xff0c;并简单的给了几个插件的demo&#xff0c;离实际项目运用其实还需要二次开发很多东西&#xff0c;其中时间mock能力是一个非常基础的能力&#xff0c;业务代码里经常需要用到这块&#xff1b; 二、调研 …

win10安装kafka,监听9092端口,java调用

1、从Kafka的官网下载Kafka安装包&#xff1a;Apache Kafka 我下的是2.8.0 对应pom.xml配置 <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>2.8.0</version> </dependency&…

linux安装配置zeppein

zeppelin是一个让交互式数据分析变得可行的基于网页的开源框架&#xff0c;具有数据分析、数据可视化等功能 一 解压安装包 这里提供了网盘资源 链接: https://pan.baidu.com/s/16pIoHL6ApGAs063cTOUYUg?pwdffq6 提取码: ffq6 下面的是 zeppein 安装包以及&#xff0c;上面的…

【爬虫实战】用python爬今日头条热榜TOP50榜单!

文章目录 一、爬取目标二、爬取结果三、代码讲解四、技术总结五、演示视频六、附完整源码 一、爬取目标 您好&#xff01;我是马哥python说&#xff0c;一名10年程序猿。 今天分享一期爬虫案例&#xff0c;爬取的目标是&#xff1a;今日头条热榜的榜单数据。 打开今日头条首…

月木学途开发 1.后台用户模块

概述 权限控制采用springsecurity 数据库设计 用户表 DROP TABLE IF EXISTS admin; CREATE TABLE admin (aid int(32) NOT NULL AUTO_INCREMENT,email varchar(50) DEFAULT NULL,username varchar(50) DEFAULT NULL,password varchar(255) DEFAULT NULL,phoneNum varchar(2…