MyBatis 反射模块

2023-09-21 11:05:55

前言

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

更多推荐

轻量级的Python IDE —— Thonny

现在的开发工具太多了,而且每个开发工具都致力于做成最好用最智能的工具,所以功能越堆越多,越怼越智能。安装这些开发工具比较烧脑,经常需要经过许多配置步骤。作为一个Python开发者来说,好多人光是这些配置都要弄半天。配置好之后,打开软件,发现满屏都是菜单、按钮,无从下手,学习这些功能使用又是一大难题。推荐一款超级轻量级的

Java源码分析(二)Double

本篇是源码分析的第二篇,上篇我们一起分析了Integer类的源码,本篇一起学习下Double类的源码,看下其实现。一、Double类图首先,相比Integer,Double类的源码只有1000+行代码。如下是Integer及其关联类/接口的类图:​通过Integer类的类图,我们总结下它的特点:Double类继承自抽象

《Linux 内核编码风格》官方手册!

这是一篇官方手册译文,分享给大家这是一个简短的文档,描述了Linux内核的首选编码风格。编码风格非常个人化,这是我必须要维护的代码(指Linux内核代码)的编码风格,对于其他项目代码,我也希望使用它。写内核代码时请至少考虑本文提出的风格。首先,我建议打印出GNU编码标准,然后不要阅读。烧掉它们,这是一个很棒的象征性动作

mariadb

1.[root@localhostsystem]#yuminstall-ymariadb-server已加载插件:fastestmirror,langpacksLoadingmirrorspeedsfromcachedhostfile*base:ftp.sjtu.edu.cn*extras:ftp.sjtu.edu.c

网络安全攻防对抗之隐藏通信隧道技术整理

完成内网信息收集工作后,渗透测试人员需要判断流量是否出得去、进得来。隐藏通信隧道技术常用于在访问受限的网络环境中追踪数据流向和在非受信任的网络中实现安全的数据传输。一、隐藏通信隧道基础知识(一)隐藏通信隧道概述一般的网络通信,先在两台机器之间建立TCP连接,然后进行正常的数据通信。在知道IP地址的情况下,可以直接发送报

Flink中的批和流

批处理的特点是有界、持久、大量,非常适合需要访问全部记录才能完成的计算工作,一般用于离线统计。流处理的特点是无界、实时,无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作,一般用于实时统计。而在Flink中,一切都是由流组成的,Flink认为有界数据集是无界数据流的一种特例,离线数据是有界限的流,实时数

MySQL如何高效实现刷脏页,了解原理并学会配置

目录一、什么是刷脏页二、MySQL刷脏页的策略三、MySQL刷脏页的实现原理四、MySQL如何实现刷脏页一、什么是刷脏页在MySQL中,刷脏页是指将内存中已被修改的数据页(也称为脏页)写回到磁盘的过程。当MySQL执行数据更新操作时,会将修改后的数据先写入内存的缓存区(称为脏页),然后通过后台线程将这些脏页定期或根据一

【MySQL】专栏合集,从基础概念到调优

作者简介前言博主之前写过一个MySQL的系列,从基础概念、SQL到底层原理、优化,专栏地址:https://blog.csdn.net/joker_zjn/category_12305262.html?spm=1001.2014.3001.5482本文会是这个系列的清单,拉通来聊一聊Mysql从基础概念一直到优化的相关

爬虫 — Js 逆向案例四网易云音乐评论

目标网站:https://music.163.com/#/song?id=2054300084需求:获取评论内容,用户名案例分析1、分析网站加载方式动态加载,抓包找到目标url:https://music.163.com/weapi/comment/resource/comments/get?csrf_token=2、

VIOOVI干货分享:什么是SOP?它的六要素是什么?

什么是SOP,SOP就是标准化的作业程序。它以文档的形式,详细的描述操作人员在生产操作过程中的操作步骤和应当遵守的事项;是操作人员的操作说明书;也是检查员指导工作的依据。SOP的六要素是:物料名称和数量:生产之前,必须确认本工作岗位所需材料与准备的材料是否一致,数量是否正确,材料是否经过IQC检验。只有在所有确认无误之

Dubbo学习(一)——dubbo学习背景

文章目录前言分布式基础理论什么是分布式系统发展演变ORMMVCRPCSOARPC(远程调用)什么是RPCRPC工作原理为什么RPC要用到DubboDubbo的优势高性能可扩展性高可靠性监控和管理使用示例总结前言分布式基础理论什么是分布式系统分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统分布式系

热文推荐