第 2 章 基础支持层(上)

在这里插入图片描述

2.1 解析器模块

常见的 XML 处理方式

  1. DOM,基于树形结构的 XML 解析方式,它会将整个 XML 文档读入内存并构建一个 DOM 树,基于这棵树形结构对各个节点(Node)进行操作。

  2. SAX,基于事件模型的 XML 解析方式,它不需要将整个 XML 文档加载到内存中,而只需将 XML 文档的一部分加载到内存中。

  3. StAX,JAXP 的后来版本,解析方式与 SAX 类似,也是把 XML 文档作为一个事件流进行处理。不同之处在于 StAX 采用的是“拉模式”。

    在这里插入图片描述

2.1.1 XPath简介

MyBatis 在初始化过程中处理 mybatis-config.xml 配置文件以及映射文件时,使用的是 DOM 解析方式,并结合使用 XPath 解析 XML 配置文件。

2.1.2 XPathParse

MyBatis 提供的 XPathParser 类封装了 XPath、Document 和 EntityResolver。

2.2 反射工具箱

2.2.1 Reflector&ReflectorFactory
  • Reflector 是 MyBatis 中反射模块的基础,每个 Reflector 对象都对应一个类,在 Reflector 中缓存了反射操作需要使用的类的元信息。
  • ReflectorFactory 接口主要实现了对 Reflector 对象的创建和缓存。

TIPS:关于 JavaBean 规范

类中定义的成员变量也称为“字段”,属性则是通过 getter/setter 方法得到的,属性只与类中的方法有关,与是否存在对应成员变量没有关系。例如,存在 getA() 方法和 setA(String) 方法,无论类中是否定义了字段 String a,我们都认为该类中存在属性 a。

2.2.2 TypeParameterResolver

TypeParameterResolver 是一个工具类,它提供了一系列静态方法来解析指定类的字段、方法返回值或方法参数的类型。

2.2.3 ObjectFactory

ObjectFactory 接口提供了多个 create() 方法的重载,通过这些方法可以创建指定类型的对象。

2.2.4 Property 工具集
  1. PropertyTokenizer

    <resultMap id="rm4testProTool" type="User">
        <id column="id" property="id"/>
        <result property="orders[0].items[0].name" column="item1"/>
        <result property="orders[0].items[1].name" column="item2"/>
    </resultMap>
    

    orders[0].items[0].name 这种由 “.” 和 “[]” 组成的表达式是由 PropertyTokenizer 进行解析的。

  2. PropertyNamer,完成方法名到属性名的转换

  3. PropertyCopier,实现相同类型的两个对象之间的属性值的拷贝

2.2.5 MetaClass

MetaClass 通过 Reflector 和 PropertyTokenizer 的组合使用,实现了对复杂的属性表达式的解析,并实现了获取指定属性描述信息的功能。

2.2.6 ObjectWrapper

ObjectWrapper 接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息的方法。

2.2.7 MetaObject

MetaObject 负责解析属性表达式。

2.3 类型转换

JDBC 数据类型与 Java 语言中的数据类型并不是完全对应的,所以在使用时需要对二者进行转换。

在这里插入图片描述

MyBatis 中使用 JdbcType 枚举代表 JDBC 中的数据类型,该枚举类型中定义了 TYPE_CODE 字段,记录了 JDBC 类型在 java.sql.Types 中相应的常量编码,并通过一个静态集合 codeLookup(HashMap<Integer, JdbcType>) 维护了常量编码与 JdbcType 之间的对应关系。

2.3.1 TypeHandler

MyBatis 中所有的类型转换器都继承了 TypeHandler 接口,在TypeHandler 接口中定义了两类四个方法,其中 setParameter() 方法将数据由 Java 类型转换成 JdbcType 类型;getResult() 方法及其重载负责将数据由 JdbcType 类型转换成 Java 类型。

2.3.2 TypeHandlerRegistry

在 MyBatis 初始化过程中,会为所有已知的 TypeHandler 创建对象,并实现注册到 TypeHandlerRegistry 中,由 TypeHandlerRegistry 负责管理这些对象。

2.3.3 TypeAliasRegistry

MyBatis 将 SQL 语句中别名的概念进行了延伸和扩展,可以为一个类添加一个别名,之后就可以通过别名引用该类。并通过 TypeAliasRegistry 完成对别名的注册和管理。

2.4 日志模块

2.4.1 适配器模式

适配器模式的主要目的是解决由于接口不能兼容而导致类无法使用的问题,适配器模式会将需要甜酸的类转换成调用者能够使用的目标接口。涉及以下角色:

  1. 目标接口(Target):调用者能够直接使用的接口
  2. 需要适配的类(Adaptee):一般情况下,Adaptee 类中有真正的业务逻辑,但是其接口不能被调用者直接使用。
  3. 适配器(Adapter):Adapter 实现了 Target 接口,并包装了一个 Adaptee 对象。Adapter 在实现 Target 接口中的方法时,会将调用委托给 Adaptee 对象,由后者完成具体的业务。

使用适配器模式的好处就是复用现有组件。这符合开闭原则,当有新的 Adaptee 需要被复用时,只要添加新的 Adapter 即可。

在 MyBatis 的日志模块中,就使用了适配器模式。MyBatis 使用内部接口(logging.Log)完成日志模块。并提供多种 Adapter 来集成和复用 Log4j,Log4j2 等三方日志组件。

2.4.2 日志适配器

MyBatis 统一提供了 trace、debug、warn、error 四个日志级别,可以满足绝大多数场景的日志需求。

2.4.3 代理模式与 JDK 动态代理

在这里插入图片描述

如图,Subject 是程序中的业务逻辑接口,RealSubject 是实现了 Subject 接口的真正业务类,Proxy 是实现了 Subject 接口的代理类,其中封装了 RealSubject 对象。程序中不会直接调用 RealSubject 对象的方法,而是使用 Proxy 对象实现相关的功能。这就是所谓的“代理模式”。

除了上述的“静态代理模式”(在编译阶段就为每个被代理类创建一个 Proxy 类),还可以使用动态代理。JDK 通过 InvocationHandler 接口,提供了动态代理的解决方案。

package com.example.chapter2.section4;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestInvokerHandler implements InvocationHandler {

    private Object target; // 真正的业务对象,也就是 RealSubject 对象

    public TestInvokerHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // ……在执行真正的业务方法之前的预处理操作……
        Object result = method.invoke(target, args);// 调用真正的业务方法
        // ……在执行真正的业务方法之后的后续处理操作……
        return result;
    }

    public Object getProxy() {
        // 创建代理对象
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
    }

    public static void main(String[] args) {
        // 创建真正的业务对象
        Subject realSubject = new RealSubject();
        // 创建 InvocationHandler 对象
        TestInvokerHandler handler = new TestInvokerHandler(realSubject);
        // 创建代理对象
        Subject proxySubject = (Subject) handler.getProxy();
        // 调用代理对象的方法
        proxySubject.operation();
    }
}
2.4.4 JDBC调试

MyBatis 日志模块中有一个 Jdbc 包,通过 JDK 动态代理的方式,将 JDBC 操作通过指定的日志框架打印出来。这个功能通常在开发阶段使用,它可以输出 SQL 语句、用户传入的绑定参数、SQL 语句影响行数等信息。

2.5 资源加载

2.5.1 类加载器简介
  1. JVM 中的类加载器(ClassLoader)负责加载来自文件系统、网络或其他来源的类文件

  2. 有三种默认使用的类加载器

    Bootstrap ClassLoader负责加载 JDK 自带的 rt.jar 包中的类文件所有类加载器的父加载器,没有任何父加载器
    Extension ClassLoader负责加载 Java 的扩展类库,即 jre/lib/ext 或 java.ext.dirs 指定的目录下的类文件System ClassLoader 的子加载器
    System(Application) ClassLoader负责从 classpath 环境变量中加载类文件,classpath 通常由 “-classpath” 或 “-cp” 命令行选项定义,或是由 JAR 中 Manifest 文件的 classpath 属性指定。Extension ClassLoader 的子加载器
  3. JVM 类加载器默认使用双亲委派模式——在加载类文件时,子加载器首先会将加载请求委托给它的父加载器。父加载器会检测自己是否已经加载过该类,如果已经加载过则加载过程结束;如果没有加载过,则请求继续向上传递直到 Bootstrap ClassLoader,最终由 Bootstrap ClassLoader 加载该类文件,如果加载失败则由子加载器尝试加载,直接发起加载请求的子加载器为止。双亲委派模式可以保证:1)子加载器可以使用父加载器已加载的类;2)父加载器已加载过的类无法被子加载器再次加载。保证了 JVM 的安全性和稳定性。

    在这里插入图片描述

  4. 可以通过继承 java.lang.ClassLoader 类的方式实现自定义类加载器,以满足特殊需要。

2.5.2 ClassLoaderWrapper

MyBatis 的 IO 包中封装了 ClassLoader 以及读取资源文件的相关 API。ClassLoaderWrapper 是一个 ClassLoader 的包装器,其中包含了多个 ClassLoader 对象。通过调整多个类加载器的使用顺序,ClassLoaderWrapper 可以确保返回 给系统使用的是正确的类加载器。

2.5.3 ResolverUtil

ResolverUtil 可以根据指定的条件查找指定包下的类,其中使用的条件由 Test 接口表示。

2.5.4 单例模式

所谓单例模式,是指在整个系统中,单例类只能有一个实例对象,且需要自行完成示例,并始终对外提供同一实例对象。

写法一:双端检锁

package com.example.chapter2.section5;

public class Singleton {
    
    // 使用 volatile 修饰 instance 变量
    private static volatile Singleton instance = null;
    
    // 私有构造方法
    private Singleton() {
    }
    
    // 静态方法
    public static Singleton getInstance() {
        // 第一次检查 instance 是否为 null
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                // 第二次检查 instance 是否为 null
                if (instance == null) {
                    // 创建实例
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

写法二:静态内部类

package com.example.chapter2.section5;

public class Singleton6 {

    // 私有的静态内部类,该静态内部类只会在newInstance()方法中被加载
    private static class SingletonHolder {
        // 静态初始化器,由JVM来保证线程安全
        private static final Singleton6 instance = new Singleton6();
    }

    // 私有构造方法
    private Singleton6() {
    }

    // 静态方法
    public static Singleton6 newInstance() {
        return SingletonHolder.instance;
    }
}
2.5.5 VFS

虚拟文件系统(Virtual File System),它用来查找指定路径下的资源。MyBatis 提供了 JBoss6VFS 和 DefaultVFS 两个 VFS 的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值