MyBatis-反射模块

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: MyBatis-反射模块

MyBatis基础模块-反射模块


image.png

www.processon.com/view/link/6…


1.反射模块


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


image.png


1.1 Reflector



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


1.1.1 属性


首先来看下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<>();
复制代码


1.1.2 构造方法


在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;
    }
}
复制代码


1.1.3 公共的API方法


然后我们可以看看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 根据名称查找属性



了解了Reflector对象的基本信息后我们需要如何来获取Reflector对象呢?在MyBatis中给我们提供了一个ReflectorFactory工厂对象。所以我们先来简单了解下ReflectorFactory对象,当然你也可以直接new 出来,像上面的案例一样,


1.2 ReflectorFactory



ReflectorFactory接口主要实现了对Reflector对象的创建和缓存


1.2.1 ReflectorFactory接口的定义


接口的定义如下


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


然后我们来看看它的具体实现


1.2.2 DefaultReflectorFactory


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);
    }
  }
}
复制代码


1.2.3 使用演示


通过上面的介绍,我们可以具体的来使用下,加深对其的理解,先准备一个JavaBean,


package com.boge.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());
    }
复制代码


1.3 Invoker



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


1.3.1 接口说明


/**
 * @author Clinton Begin
 */
public interface Invoker {
  // 执行Field或者Method
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
  // 返回属性相应的类型
  Class<?> getType();
}
复制代码


该接口有对应的三个实现


image.png

1.3.2 效果演示


使用效果演示,还是通过上面的案例来介绍


package com.boge.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);
    }
复制代码



1.4 MetaClass


在Reflector中可以针对普通的属性操作,但是如果出现了比较复杂的属性,比如 private Person person; 这种,我们要查找的表达式 person.userName.针对这种表达式的处理,这时就可以通过MetaClass来处理了。我们来看看主要的属性和构造方法


/**
 * 通过 Reflector 和 ReflectorFactory 的组合使用 实现对复杂的属性表达式的解析
 * @author Clinton Begin
 */
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对象


package com.boge.domain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RichType {
    private RichType richType;
    private String richField;
    private String richProperty;
    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 String getRichProperty() {
        return richProperty;
    }
    public void setRichProperty(String richProperty) {
        this.richProperty = richProperty;
    }
    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("richField"));
        System.out.println(meta.hasGetter("richProperty"));
        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.richField"));
        System.out.println(meta.hasGetter("richType.richProperty"));
        System.out.println(meta.hasGetter("richType.richList"));
        System.out.println(meta.hasGetter("richType.richMap"));
        System.out.println(meta.hasGetter("richType.richList[0]"));
        // findProperty 只能处理 . 的表达式
        System.out.println(meta.findProperty("richType.richProperty"));
        System.out.println(meta.findProperty("richType.richProperty1"));
        System.out.println(meta.findProperty("richList[0]"));
        System.out.println(Arrays.toString(meta.getGetterNames()));
    }
复制代码


输出结果


true
true
true
true
true
true
true
true
true
true
true
richType.richProperty
richType.
null
[richType, richProperty, richMap, richList, richField]
复制代码



1.5 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"));
    }
    @Test
    public void shouldGetAndSetNestedField() {
        RichType rich = new RichType();
        MetaObject meta = SystemMetaObject.forObject(rich);
        meta.setValue("richType.richField", "foo");
        System.out.println(meta.getValue("richType.richField"));
    }
    @Test
    public void shouldGetAndSetMapPairUsingArraySyntax() {
        RichType rich = new RichType();
        MetaObject meta = SystemMetaObject.forObject(rich);
        meta.setValue("richMap[key]", "foo");
        System.out.println(meta.getValue("richMap[key]"));
    }
复制代码


以上三个方法的输出结果都是


foo
复制代码




1.6 反射模块应用


然后我们来看下在MyBatis的核心处理层中的实际应用


1.6.1 SqlSessionFactory


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


image.png

然后在解析全局配置文件的代码中,给用户提供了ReflectorFactory的扩展,也就是我们在全局配置文件中可以通过


reflectorFactory标签来使用我们自定义的ReflectorFactory


1.6.2 SqlSession


无相关操作


1.6.3 Mapper


无相关操作


1.6.4 执行SQL


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


image.png

然后在DefaultResultSetHandler的getRowValue方法中在做自动映射的时候

image.png

继续跟踪,在createAutomaticMappings方法中

image.png

当然还有很多其他的地方在使用反射模块来完成的相关操作,这些可自行查阅



相关文章
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
70 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
2月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
487 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
SQL 缓存 Java
MyBatis 缓存模块
MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,Mybatis中的缓存分为一级缓存和二级缓存。但本质上是一样的,都是使用Cache接口实现的。
46 0
|
7月前
|
安全 前端开发 Java
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)3
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
127 0
|
7月前
|
Java 数据库连接 API
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)2
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
93 0
|
7月前
|
存储 Java 数据库连接
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)1
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
92 0
|
SQL Java 数据库连接
MyBatis 类型转换模块
MyBatis是一个持久层框架ORM框架,实现数据库中数据和Java对象中的属性的双向映射,那么不可避免的就会碰到类型转换的问题,在PreparedStatement为SQL语句绑定参数时,需要从Java类型转换为JDBC类型,而从结果集中获取数据时,则需要从JDBC类型转换为Java类型,所以本文来看下在MyBatis中是如何实现类型的转换的。
67 0
|
Dubbo IDE NoSQL
SpringBoot+Mybatis多模块(module)项目搭建教程
SpringBoot+Mybatis多模块(module)项目搭建教程
441 0
SpringBoot+Mybatis多模块(module)项目搭建教程
|
设计模式 安全 算法
Mybatis源码分析三-数据源模块分析,工厂模式的使用
Mybatis源码分析三-数据源模块分析,工厂模式的使用
111 0
|
SQL Java 数据库连接
MyBatis-类型转换模块
MyBatis-类型转换模块
MyBatis-类型转换模块