MyBatis源码剖析

[MyBatis源码分析 - 反射器模块]

2020-10-26  本文已影响0人  小胡_鸭

一、背景介绍

  MyBatis作为一个ORM框架,一个核心的功能是数据库数据和Java POJO(即Java Bean)之间的转换,MyBatis是基于JDBC封装的,先来看一段简单的JDBC代码。

    public Role getRole(Long id) {
        Connection connection = getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = connection.prepareStatement("select id, role_name, note from t_role where id = ?");
            ps.setLong(1, id);
            rs = ps.executeQuery();
            while (rs.next()) {
                Long roleId = rs.getLong("id");
                String roleName = rs.getString("role_name");
                String note = rs.getString("note");
                Role role = new Role();
                role.setId(id);
                role.setRoleName(roleName);
                role.setNote(note);
                return role;
            }
        } catch (Exception e) {
            Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, e);
        } finally {
            this.close(rs, ps, connection);
        }
        return null;
    }

  从上面代码可以看到JDBC执行一条带参数的SQL时,要有传入的参数值id,还要知道参数值的类型Long,这样才知道要调用PreparedStatement对象的setLong方法。MyBatis执行SQL时,一般传入的参数类型是一个Java Bean,设置SQL参数往往是用到Java Bean的部分成员属性,获取参数值通过属性的getter方法获取,属性类型需要通过反射获取,一般简单使用的Java Bean的属性是跟数据库数据表的字段对应的,eg:

public class Role {
    private long id;
    private String roleName;
    private String note;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note;
    }
}
POJO对应的数据表结构
  当然,在更加复杂的业务模型中,POJO和表结构不是简单的一对一关系,可能一个POJO组合在另一个POJO中,常见于表关联产生的一对一、一对多映射中。POJO成员属性的数据类型虽然常见是基本类型的包装类(不建议直接使用基本类型如intlong,而是使用它们的包装类IntegerLong,因为基本类型会有默认值,而包装类型是对象默认为null),但也有可能是集合类型如List(比如一对多一个订单下有多个商品),还可能是Map(当然Map更多用在传参而不是成员属性类型或者POJO类型这种语义不强的场景)。

  如果业务模型注重建模,POJO可能还会有继承关系,继承意味子类也继承了父类的属性,还有更复杂的情况,为了灵活使用,一些父类中的属性类型可能会被设计成泛型,形式可能为<T>(普通泛型)、<T extends Xxx><T super Yyy>(限制泛型)、<? extends Xxx><? super Yyy>(通配符泛型),这些都增加了POJO和数据表数据转换的难度,因为执行SQL设置参数、SQL执行结果映射转化为Java对象,都需要知道实际使用时属性的确切类型,还有调用相应属性的settergetter,这些都需要依赖Java的反射API来实现。

Java反射API虽然功能强大,但编写出高质量的反射代码难度较高,比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射模块,该模块对 Java 原生的反射进行了良好的封装,提了更加简洁易用的 API,方便上层使调用,并且对反射操作进行了一系列优化,比如将类的元信息封装缓存在MetaClass对象中,提高了反射操作的性能。

  反射模块的代码一览如下:


二、功能概述

  根据上面的背景介绍和需求分析总结,反射器模块需要实现的功能有:

  关于相关组件介绍的系列文章如下:

三、Reflector

  Reflector是整个反射器模块的基础,通过其解析类的元信息并缓存起来,源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件一] Reflector

四、Invoker

  Invoker 是一个接口,缓存了类属性对应的 Field 对象和 getter/setter 方法对应的 Method 对象,设置获取属性值、调用方法,实际上是调用 Field 对象和 Method 对象的反射方法去完成,Reflector 中的 getMethodssetMethods 属性缓存了属性和对应的 Invoker 对象的映射,源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件二] Invoker

五、ObjectFactory

  ObjectFactory 从名字上看很容器猜出用途,对象工厂就是用来创建对象的,MyBatis 中会使用该类来创建所有需要的新对象,源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件三] ObjectFactory

六、TypeParameterResolver

  TypeParameterResolver 是一个类型参数解析器,即JavaBean中可能带泛型如 List<T>、通配符如 <? super SubClass>,而且可能复杂的继承关系,为了获取到运行时正确具体的类型(包括属性类型、方法参数类型、方法返回值类型),需要使用该类解析类型参数,源码解析详见:[[MyBatis源码分析 - 反射器模块 - 组件四] TypeParameterResolver]

七、Property 工具集

  主要有三个工具类:PropertyCopierPropertyNamerPropertyTokenizer。分别用来拷贝对象属性值、从方法名提取属性信息、解析属性表达式,源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件五] Property 工具集

八、MetaClass

  MetaClass 通过对 ReflectorPropertyTokenizer 组合使用,实现了对复杂的属性表达式的解析,并实现了获取指定属性描述信息的功能,源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件六] MetaClass

九、ObjectWrapper

  ObjectWrapper 接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息、以及更新属性的方法,源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件七] ObjectWrapper

十、MetaObject

  MetaObject 封装了 ObjectWrapper 对象和相应配套的工厂对象,主要联合实现对表达式的解析和配套的操作,形成一组完成的操作对象的方法,其源码解析详见:[MyBatis源码分析 - 反射器模块 - 组件八] MetaObject

上一篇 下一篇

猜你喜欢

热点阅读