基础支持层
在《精尽 MyBatis 源码分析 - 整体架构》中对 MyBatis 的基础支持层已做过介绍,包含整个 MyBatis 的基础模块,为核心处理层的功能提供了良好的支撑,本文对基础支持层的每个模块进行分析
- 解析器模块
- 反射模块
- 异常模块
- 数据源模块
- 事务模块
- 缓存模块
- 类型模块
- IO模块
- 日志模块
- 注解模块
- Binding模块
解析器模块
主要包路径:org.apache.ibatis.parsing
主要功能:初始化时解析mybatis-config.xml配置文件、为处理动态SQL语句中占位符提供支持
主要查看以下几个类:
org.apache.ibatis.parsing.XPathParser
:基于Java XPath 解析器,用于解析MyBatis的mybatis-config.xml和**Mapper.xml等XML配置文件org.apache.ibatis.parsing.GenericTokenParser
:通用的Token解析器org.apache.ibatis.parsing.PropertyParser
:动态属性解析器
XPathParser
org.apache.ibatis.parsing.XPathParser
:基于Java XPath 解析器,用于解析MyBatis的mybatis-config.xml和**Mapper.xml等XML配置文件
主要代码如下:
public class XPathParser {
/**
* XML Document 对象
*/
private final Document document;
/**
* 是否检验
*/
private boolean validation;
/**
* XML实体解析器
*/
private EntityResolver entityResolver;
/**
* 变量对象
*/
private Properties variables;
/**
* Java XPath 对象
*/
private XPath xpath;
public XPathParser(String xml) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public String evalString(String expression) {
return evalString(document, expression);
}
public String evalString(Object root, String expression) {
// <1> 获得值
String result = (String) evaluate(expression, root, XPathConstants.STRING);
// <2> 基于 variables 替换动态值,如果 result 为动态值
result = PropertyParser.parse(result, variables);
return result;
}
private Object evaluate(String expression, Object root, QName returnType) {
try {
// 通过XPath结合表达式获取Document对象中的结果
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
public XNode evalNode(String expression) {
return evalNode(document, expression);
}
public XNode evalNode(Object root, String expression) {
// <1> 获得 Node 对象
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
// <2> 封装成 XNode 对象
return new XNode(this, node, variables);
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
// 1> 创建 DocumentBuilderFactory 对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
// 2> 创建 DocumentBuilder 对象
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver); // 设置实体解析器
builder.setErrorHandler(new ErrorHandler() { // 设置异常处理,实现都空的
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
// NOP
}
});
// 3> 解析 XML 文件,将文件加载到Document中
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
}
看到定义的几个属性:
类型 | 属性名 | 说明 |
Document | document | XML文件被解析后生成对应的 |
boolean | validation | 是否校验XML文件,一般情况下为true |
EntityResolver | entityResolver |
|
Properties | variables | 变量Properties对象,用来替换需要动态配置的属性值,例如我们在MyBatis的配置文件中使用变量将用户名密码放在另外一个配置文件中,那么这个配置会被解析到Properties对象用,用于替换XML文件中的动态值 |
XPath | xpath |
|
构造函数有很多,基本都相似,内部都是调用commonConstructor
方法设置相关属性和createDocument
方法为该XML文件创建一个Document对象
提供了一系列的eval*
方法,用于获取Document对象中的元素或者节点:
- eval*元素的方法:根据表达式获取我们常用类型的元素的值,其中会基于
variables
调用PropertyParser
的parse
方法替换掉其中的动态值(如果存在),这就是MyBatis如何替换掉XML中的动态值实现的方式 - eval*节点的方法:根据表达式获取到
org.w3c.dom.Node
节点对象,将其封装成自己定义的XNode
对象,方便主要为了动态值的替换
PropertyParser
org.apache.ibatis.parsing.PropertyParser
:动态属性解析器
主要代码如下:
public class PropertyParser {
public static String parse(String string, Properties variables) {
// <2.1> 创建 VariableTokenHandler 对象
VariableTokenHandler handler = new VariableTokenHandler(variables);
// <2.2> 创建 GenericTokenParser 对象
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
// <2.3> 执行解析
return parser.parse(string);
}
}
parse
方法:创建VariableTokenHandler对象和GenericTokenParser对象,然后调用GenericTokenParser的parse方法替换其中的动态值
GenericTokenParser
org.apache.ibatis.parsing.GenericTokenParser
:通用的Token解析器
定义了是三个属性:
public class GenericTokenParser {
/**
* 开始的 Token 字符串
*/
private final String openToken;
/**
* 结束的 Token 字符串
*/
private final String closeToken;
/**
* Token处理器
*/
private final TokenHandler handler;
}
根据开始字符串和结束字符串解析出里面的表达式(例如${name}->name),然后通过TokenHandler进行解析处理
VariableTokenHandler
VariableTokenHandler
,是PropertyParser
的内部静态类,变量Token处理器,根据Properties variables
变量对象将Token动态值解析成实际值
总结
- 将XML文件解析成
XPathParser
对象,其中会解析成对应的Document
对象,内部的Properties对象存储动态变量的值 PropertyParser
用于解析XML文件中的动态值,根据GenericTokenParser
获取动态属性的名称(例如${name}->name),然后通过VariableTokenHandler
根据Properties对象获取到动态属性(name)对应的值
反射模块
主要功能:对Java原生的反射进行了良好的封装,提供更加简单易用的API,用于解析类对象
反射这一模块的代码写得很漂亮,值得参考!!!
主要包路径:org.apache.ibatis.reflection
如下所示:
主要查看以下几个类:
org.apache.ibatis.reflection.Reflector
:保存Class类中定义的属性相关信息并进行了简单的映射org.apache.ibatis.reflection.invoker.MethodInvoker
:Class类中属性对应set方法或者get方法的封装org.apache.ibatis.reflection.DefaultReflectorFactory
:Reflector的工厂接口,用于创建和缓存Reflector对象org.apache.ibatis.reflection.MetaClass
:Class类的元数据,包装Reflector,基于PropertyTokenizer(分词器)提供对Class类的元数据一些操作,可以理解成对Reflector操作的进一步增强org.apache.ibatis.reflection.DefaultObjectFactory
:实现了ObjectFactory工厂接口,用于创建Class类对象org.apache.ibatis.reflection.wrapper.BeanWrapper
:实现了ObjectWrapper对象包装接口,继承BaseWrapper抽象类,根据Object对象与其MetaClass元数据对象提供对该Object对象的一些操作org.apache.ibatis.reflection.MetaObject
:对象元数据,提供了操作对象的属性等方法。 可以理解成对ObjectWrapper操作的进一步增强org.apache.ibatis.reflection.SystemMetaObject
:用于创建MetaObject对象,提供了ObjectFactory、ObjectWrapperFactory、空MetaObject的单例org.apache.ibatis.reflection.ParamNameResolver
:方法参数名称解析器,用于解析我们定义的Mapper接口的方法
Reflector
org.apache.ibatis.reflection.Reflector
:保存Class类中定义的属性相关信息并进行了简单的映射
部分代码如下:
public class Reflector {
/**
* Class类
*/
private final Class<?> type;
/**
* 可读属性集合
*/
private final String[] readablePropertyNames;
/**
* 可写属性集合
*/
private final String[] writablePropertyNames;
/**
* 属性对应的 setter 方法的映射。
*
* key 为属性名称
* value 为 Invoker 对象
*/
private final Map<String, Invoker> setMethods = new HashMap<>();
/**
* 属性对应的 getter 方法的映射。
*
* key 为属性名称 value 为 Invoker 对象
*/
private final Map<String, Invoker> getMethods = new HashMap<>();
/**
* 属性对应的 setter 方法的方法参数类型的映射。{@link #setMethods}
*
* key 为属性名称
* value 为方法参数类型
*/
private final Map<String, Class<?>> setTypes = new HashMap<>();
/**
* 属性对应的 getter 方法的返回值类型的映射。{@link #getMethods}
*
* key 为属性名称
* value 为返回值的类型
*/
private final Map<String, Class<?>> getTypes = new HashMap<>();
/**
* 默认构造方法
*/
private Constructor<?> defaultConstructor;
/**
* 所有属性集合
* key 为全大写的属性名称
* value 为属性名称
*/
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
public Reflector(Class<?> clazz) {
// 设置对应的类
type = clazz;
// <1> 初始化 defaultConstructor 默认构造器,也就是无参构造器
addDefaultConstructor(clazz);
// <2> 初始化 getMethods 和 getTypes
addGetMethods(clazz);
// <3> 初始化 setMethods 和 setTypes
addSetMethods(clazz);
// <4> 可能有些属性没有get或者set方法,则直接将该Field字段封装成SetFieldInvoker或者GetFieldInvoker,然后分别保存至上面4个变量中
addFields(clazz);
// <5> 初始化 readablePropertyNames、writeablePropertyNames、caseInsensitivePropertyMap 属性
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
}
通过上面的代码可以看到Reflector
在初始化的时候会通过反射机制进行解析该Class类,整个解析过程并不复杂,我这里就不全部讲述了,可阅读相关代码,已做好注释😈😈😈
解析后保存了Class类的以下信息:
类型 | 字段 | 说明 |
Class<?> | type | Class类 |
String[] | readablePropertyNames | 可读属性集合 |
String[] | writablePropertyNames | 可写属性集合 |
Map<String, Invoker> | setMethods | 属性对应的 setter 方法的映射:<属性名称, MethodFieldInvoker对象> |
Map<String, Invoker> | getMethods | 属性对应的 getter 方法的映射:<属性名称, MethodFieldInvoker对象> |
Map<String, Class<?>> | setTypes | 属性对应的 setter 方法的方法参数类型的映射:<属性名称, 方法参数类型> |
Map<String, Class<?>> | getTypes | 属性对应的 getter 方法的返回值类型的映射:<属性名称, 方法返回值类型> |
Constructor<?> | defaultConstructor | 默认构造方法 |
Map<String, String> | caseInsensitivePropertyMap | 所有属性集合:<属性名称(全大写), 属性名称> |
MethodInvoker
org.apache.ibatis.reflection.invoker.MethodInvoker
:Class类中属性对应set方法或者get方法的封装
代码如下:
public class MethodInvoker implements Invoker {
/**
* 类型
*/
private final Class<?> type;
/**
* 指定方法
*/
private final Method method;
public MethodInvoker(Method method) {
this.method = method;
if (method.getParameterTypes().length == 1) {
// 参数大小为 1 时,一般是 setter 方法,设置 type 为方法参数[0]
type = method.getParameterTypes()[0];
} else {
// 否则,一般是 getter 方法,设置 type 为返回类型
type = method.getReturnType();
}
}
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
method.setAccessible(true);
return method.invoke(target, args);
} else {
throw e;
}
}
}
@Override
public Class<?> getType() {
return type;
}
}
- 在其构造函数中,设置set方法或者get方法,并获取其参数类型或者返回值类型进行保存,也就是该属性的类型
- 如果Class类中有些属性没有set或者get方法,那么这些属性会被封装成下面两个对象(final static修饰的字段不会被封装),用于设置或者获取他们的值
org.apache.ibatis.reflection.invoker.SetFieldInvoker
、org.apache.ibatis.reflection.invoker.GetFieldInvoker
DefaultReflectorFactory
org.apache.ibatis.reflection.DefaultReflectorFactory
:Reflector的工厂接口,用于创建和缓存Reflector对象
代码如下:
public class DefaultReflectorFactory implements ReflectorFactory {
/**
* 是否缓存
*/
private boolean classCacheEnabled = false;
/**
* Reflector 的缓存映射
*
* KEY:Class 对象
* VALUE: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);
}
}
}
根据Class对象创建Reflector对象,代码比较简单
MetaClass
org.apache.ibatis.reflection.MetaClass
:Class类的元数据,包装Reflector,基于PropertyTokenizer(分词器)提供对Class类的元数据各种操作,可以理解成对Reflector操作的进一步增强
其中包含了ReflectorFactory和Reflector两个字段,通过PropertyTokenizer分词器提供了获取属性的名称和返回值类型等等方法,也就是在Reflector上面新增了一些方法
org.apache.ibatis.reflection.propertyPropertyTokenizer
分词器用于解析类似于'map[qm].user'这样的属性,将其分隔保存方便获取,可阅读相关代码哦😈😈😈
DefaultObjectFactory
org.apache.ibatis.reflection.DefaultObjectFactory
:实现了ObjectFactory工厂接口,用于创建Class类对象
部分代码如下:
public class DefaultObjectFactory implements ObjectFactory, Serializable {
@Override
public <T> T create(Class<T> type) {
return create(type, null, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// 获取需要创建的类
Class<?> classToCreate = resolveInterface(type);
// 创建指定类的对象
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
protected Class<?> resolveInterface(Class<?> type) {
Class<?> classToCreate;
if (type == List.class || type == Collection.class || type == Iterable.class) {
classToCreate = ArrayList.class;
} else if (type == Map.class) {
classToCreate = HashMap.class;
} else if (type == SortedSet.class) { // issue #510 Collections Support
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
return classToCreate;
}
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
if (constructorArgTypes == null || constructorArgs == null) {
// 使用默认的构造器
constructor = type.getDeclaredConstructor();
try {
// 返回实例
return constructor.newInstance();
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance();
} else {
throw e;
}
}
}
// 通过参数类型列表获取构造器
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
try {
// 返回实例
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} else {
throw e;
}
}
} catch (Exception e) {
...
}
}
}
通过Class对象获取构造函数,然后通过构造函数创建一个实例对象
BeanWrapper
org.apache.ibatis.reflection.wrapper.BeanWrapper
:实现了ObjectWrapper对象包装接口,继承BaseWrapper抽象类,根据Object对象与其MetaClass元数据对象提供对该Object对象的一些操作,部分代码如下:
public class BeanWrapper extends BaseWrapper {
/**
* 普通对象
*/
private final Object object;
private final MetaClass metaClass;
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
this.object = object;
// 创建 MetaClass 对象
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
@Override
public Object get(PropertyTokenizer prop) {
// <1> 获得集合类型的属性的指定位置的值
if (prop.getIndex() != null) {
// 获得集合类型的属性
Object collection = resolveCollection(prop, object);
// 获得指定位置的值
return getCollectionValue(prop, collection);
// <2> 获得属性的值
} else {
return getBeanProperty(prop, object);
}
}
@Override
public void set(PropertyTokenizer prop, Object value) {
// 设置集合类型的属性的指定位置的值
if (prop.getIndex() != null) {
// 获得集合类型的属性
Object collection = resolveCollection(prop, object);
// 设置指定位置的值
setCollectionValue(prop, collection, value);
} else { // 设置属性的值
setBeanProperty(prop, object, value);
}
}
}
get
方法:根据分词器从object
对象中获取对应的属性值
set
方法:根据分词器往object
对象中设置属性值
还包含了其他很多操作object
对象的方法,这里不全部罗列出来了,请阅读其相关代码进行查看😈😈😈
MetaObject
org.apache.ibatis.reflection.MetaObject
:对象元数据,提供了操作对象的属性等方法,可以理解成对 ObjectWrapper 操作的进一步增强
在Mybatis中如果需要操作某个对象(实例类或者集合),都会转换成MetaObject类型,便于操作
主要代码如下:
public class MetaObject {
/**
* 原始 Object 对象
*/
private final Object originalObject;
/**
* 封装过的 Object 对象
*/
private final ObjectWrapper objectWrapper;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory reflectorFactory;
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory,
ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
/**
* 创建 MetaObject 对象
*
* @param object 原始 Object 对象
* @param objectFactory 生产 Object 的实例工厂
* @param objectWrapperFactory 创建 ObjectWrapper 工厂,没有默认实现,没有用到
* @param reflectorFactory 创建 Object 对应 Reflector 的工厂
* @return MetaObject 对象
*/
public static MetaObject forObject(Object object, ObjectFactory objectFactory,
ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
public String findProperty(String propName, boolean useCamelCaseMapping) {
return objectWrapper.findProperty(propName, useCamelCaseMapping);
}
/**
* 获取指定属性的值,递归处理
*
* @param name 属性名称
* @return 属性值
*/
public Object getValue(String name) {
// 创建 PropertyTokenizer 对象,对 name 分词
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) { // 有子表达式
// 创建 MetaObject 对象
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
// <2> 递归判断子表达式 children ,获取值
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else { // 无子表达式
// <1> 获取值
return objectWrapper.get(prop);
}
}
/**
* 设置指定属性值
*
* @param name 属性名称
* @param value 属性值
*/
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null) {
// don't instantiate child path if value is null
return;
} else {
// <1> 创建值
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
} else {
// <1> 设置值
objectWrapper.set(prop, value);
}
}
/**
* 创建指定属性的 MetaObject 对象
*
* @param name 属性名称
* @return MetaObject 对象
*/
public MetaObject metaObjectForProperty(String name) {
// 获得属性值
Object value = getValue(name);
// 创建 MetaObject 对象
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
我们可以看到构造方法是私有的,无法直接通过构造函数创建实例对象,提供了一个forObject
静态方法来创建一个MetaObject对象:
- 如果原始Object对象为null,则返回空的MetaObject对象
NULL_META_OBJECT
,在SystemMetaObject
中定义的一个单例对象,实际就是将MetaObject内部的Object原始对象设置为NullObject
(一个静态类) - 否则通过构造函数创建MetaObject对象,在它的构造函数中可以看到,根据Object对象的类型来决定创建什么类型的
ObjectWrapper
,并没有用到ObjectWrapperFactory
工厂接口(默认实现也抛出异常)
对于一个已经初始化好的MetaObject对象,可以通过getValue
方法获取指定属性的值,setValue
设置指定属性值
SystemMetaObject
org.apache.ibatis.reflection.SystemMetaObject
:系统级别的MetaObject对象,提供了ObjectFactory、ObjectWrapperFactory、空MetaObject的单例
代码如下:
public final class SystemMetaObject {
/**
* ObjectFactory 的单例
*/
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
/**
* ObjectWrapperFactory 的单例
*/
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
/**
* 空对象的 MetaObject 对象单例
*/
public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY,
DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
private SystemMetaObject() {
// Prevent Instantiation of Static Class
}
private static class NullObject {
}
/**
* 创建 MetaObject 对象
*
* @param object 指定对象
* @return MetaObject 对象
*/
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,
new DefaultReflectorFactory());
}
}
内部就定义了一个forObject(Object object)
静态方法,用于创建MetaObject对象
我们一般会将Entity实体类或者Map集合解析成MetaObject对象,然后可以对其属性进行操作
ParamNameResolver
org.apache.ibatis.reflection.ParamNameResolver
:方法参数名称解析器,用于解析我们定义的Mapper接口的方法
在org.apache.ibatis.binding.MapperMethod
的MethodSignature
内部类会用到
主要代码如下:
public class ParamNameResolver {
private static final String GENERIC_NAME_PREFIX = "param";
/**
* 参数名映射
* KEY:参数顺序
* VALUE:参数名
*/
private final SortedMap<Integer, String> names;
/**
* 是否有 {@link Param} 注解的参数
*/
private boolean hasParamAnnotation;
public ParamNameResolver(Configuration config, Method method) {
// 获取方法的参数类型集合
final Class<?>[] paramTypes = method.getParameterTypes();
// 获取方法的参数上面的注解集合
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 忽略 RowBounds、ResultHandler参数类型
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
// <1> 首先,从 @Param 注解中获取参数名
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
// <2> 其次,获取真实的参数名
if (config.isUseActualParamName()) { // 默认开启
name = getActualParamName(method, paramIndex);
}
// <3> 最差,使用 map 的顺序,作为编号
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
// 添加到 map 中
map.put(paramIndex, name);
}
// 构建不可变的 SortedMap 集合
names = Collections.unmodifiableSortedMap(map);
}
/**
* 根据参数值返回参数名称与参数值的映射关系
*
* @param args 参数值数组
* @return 参数名称与参数值的映射关系
*/
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
// 无参数,则返回 null
if (args == null || paramCount == 0) {
return null;
// 只有1个参数,并且没有 @Param 注解,则直接返回该值
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
/*
* 参数名称与值的映射,包含以下两种组合数据:
* 组合1:(参数名,值)
* 组合2:(param+参数顺序,值)
*/
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 组合 1 :添加到 param 中
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
// 组合 2 :添加到 param 中
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
在构造函数中可以看到,目的是获取到该方法的参数名,将参数顺序与参数名进行映射保存在UnmodifiableSortedMap
一个不可变的SortedMap集合中,大致逻辑:
- 如果添加了
@Param
注解,则参数名称为该注解的value值 - 没有添加@Param注解则尝试获取真实的参数名
说明:通过反射获取方法的参数名,我们只能获取到 arg0,arg1 的名称,这些变量名称没有被编译到class文件中,在jdk8之后,编译时我们可以指定
-parameters
选项,方法的参数名才会记录到class文件中,运行时我们就可以通过反射机制获取到
- 还是没有获取到参数名则使用序号标记,一般不会走到这一步
还有一个getNamedParams
方法,根据实际入参数组返回参数名与参数值的映射,大致逻辑:
- 实际参数为null或者参数个数为0,则直接返回null
- 没有使用
@Param
注解并且参数个数为1,则直接返回参数值 - 根据参数顺序与参数名的映射获取到
参数名与参数值的映射
,而且还会将(param+参数顺序)与参数值进行映射
,最后将两种组合的映射返回
总结
- 将一个Entity实体类或者Map集合转换成MetaObject对象,该对象通过反射机制封装了各种简便的方法,使更加方便安全地操作该对象,创建过程:
- 通过
Configuration
全局配置对象的newMetaObject(Object object)
方法创建,会传入DefaultObjectFactory
、DefaultObjectWrapperFactory
和DefaultReflectorFactory
几个默认实现类- 内部调用
MetaObject
的forObject
静态方法,通过它的构造方法创建一个实例对象- 在MetaObject的构造函数中,会根据Object对象的类型来创建
ObjectWrapper
对象- 如果是创建
BeanWrapper
,则在其构造函数中,会再调用MetaClass的forClass方法创建MetaClass
对象,也就是通过其构造函数创建一个实例对象- 如果是
MapWrapper
,则直接复制给内部的Map<String, Object> map
属性即可,其他集合对象类似- 在MetaClass的构造函数中,会通过调用
DefaultReflectorFactory
的findForClass方法创建Reflector
对象- 在Reflector的构造函数中,通过反射机制解析该Class类,属性的set和get方法会被封装成
MethodInvoker
对象
- ParamNameResolver工具类提供方法参数解析功能(主要解析Mapper接口里面的方法参数哦~)
异常模块
MyBatis的几个基本的Exception异常类在org.apache.ibatis.exceptions包路径下
org.apache.ibatis.exceptions.IbatisException
:实现 RuntimeException 类,MyBatis 的异常基类org.apache.ibatis.exceptions.PersistenceException
:继承 IbatisException 类,目前 MyBatis 真正的异常基类org.apache.ibatis.exceptions.ExceptionFactory
:异常工厂
每个模块都有自己都有的异常类,代码都是相同的,这里就不一一展示了
数据源模块
MyBatis支持三种数据源配置,分别为UNPOOLED
、POOLED
和JNDI
。内部提供了两种数据源实现,分别是UnpooledDataSource
和PooledDataSource
。在三种数据源配置中,UNPOOLED 和 POOLED 是常用的两种配置。至于 JNDI,MyBatis 提供这种数据源的目的是为了让其能够运行在 EJB 或应用服务器等容器中,这一点官方文档中有所说明。由于 JNDI 数据源在日常开发中使用甚少,因此,本篇文章不打算分析 JNDI 数据源相关实现。大家若有兴趣,可自行分析。
实际场景下,我们基本不用 MyBatis 自带的数据库连接池的实现,这里是让我们是对数据库连接池的实现有个大体的理解。
主要包路径:org.apache.ibatis.datasource
主要功能:数据源的实现,与MySQL连接的管理
主要查看以下几个类:
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
:实现 DataSourceFactory 接口,非池化的 DataSourceFactory 实现类org.apache.ibatis.datasource.unpooled.UnpooledDataSource
:实现 DataSource 接口,非池化的 DataSource 对象org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
:继承 UnpooledDataSourceFactory 类,池化的 DataSourceFactory 实现类org.apache.ibatis.datasource.pooled.PooledDataSource
:实现 DataSource 接口,池化的 DataSource 实现类org.apache.ibatis.datasource.pooled.PoolState
:连接池状态,记录空闲和激活的 PooledConnection 集合,以及相关的数据统计org.apache.ibatis.datasource.pooled.PooledConnection
:实现 InvocationHandler 接口,池化的 Connection 对象
UnpooledDataSourceFactory
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
:实现 DataSourceFactory 接口,非池化的 DataSourceFactory 实现类
代码如下:
public class UnpooledDataSourceFactory implements DataSourceFactory {
private static final String DRIVER_PROPERTY_PREFIX = "driver.";
private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
protected DataSource dataSource;
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
@Override
public void setProperties(Properties properties) {
Properties driverProperties = new Properties();
// 创建 dataSource 对应的 MetaObject 对象,其中是BeanWrapper
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
// 遍历 properties 属性,初始化到 driverProperties 和 MetaObject 中
for (Object key : properties.keySet()) {
String propertyName = (String) key;
// 初始化到 driverProperties 中
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
// 如果该属性在UnpooledDataSource中有setter方法,则初始化到 MetaObject 中
} else if (metaDataSource.hasSetter(propertyName)) {
String value = (String) properties.get(propertyName);
Object convertedValue = convertValue(metaDataSource, propertyName, value);
// 将数据设置到UnpooledDataSource中去
metaDataSource.setValue(propertyName, convertedValue);
} else {
throw new DataSourceException("Unknown DataSource property: " + propertyName);
}
}
if (driverProperties.size() > 0) {
// 设置 driverProperties 到 MetaObject 中
metaDataSource.setValue("driverProperties", driverProperties);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
Object convertedValue = value;
// 获得该属性的 setting 方法的参数类型
Class<?> targetType = metaDataSource.getSetterType(propertyName);
// 转化
if (targetType == Integer.class || targetType == int.class) {
convertedValue = Integer.valueOf(value);
} else if (targetType == Long.class || targetType == long.class) {
convertedValue = Long.valueOf(value);
} else if (targetType == Boolean.class || targetType == boolean.class) {
convertedValue = Boolean.valueOf(value);
}
return convertedValue;
}
}
setProperties(Properties properties)
方法:
- 该
dataSource
创建对应的MetaObject
对象,便于设置相应的属性 - 将入参
Properties
对象中的配置往dataSource
设置 - 如果是以
driver.
开头的配置则统一放入一个Properties中,然后设置到dataSource
的driverProperties
属性中
getDataSource()
方法:直接返回创建好的数据源dataSource
UnpooledDataSource
org.apache.ibatis.datasource.unpooled.UnpooledDataSource
:实现 DataSource 接口,非池化的 DataSource 对象
获取数据库连接的方法:
public class UnpooledDataSource implements DataSource {
private Connection doGetConnection(String username, String password) throws SQLException {
// 创建 Properties 对象
Properties props = new Properties();
// 设置 driverProperties 到 props 中
if (driverProperties != null) {
props.putAll(driverProperties);
}
// 设置 user 和 password 到 props 中
if (username != null) {
props.setProperty("user", username);
}
if (password != null) {
props.setProperty("password", password);
}
return doGetConnection(props);
}
private Connection doGetConnection(Properties properties) throws SQLException {
// <1> 初始化 Driver
initializeDriver();
// <2> 获得 Connection 对象
Connection connection = DriverManager.getConnection(url, properties);
// <3> 配置 Connection 对象
configureConnection(connection);
return connection;
}
private synchronized void initializeDriver() throws SQLException {
// 判断 registeredDrivers 是否已经存在该 driver ,若不存在,进行初始化
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
// <2> 获得 driver 类
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
// <3> 创建 Driver 对象
Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
// 创建 DriverProxy 对象(为了使用自己定义的Logger对象),并注册到 DriverManager 中
DriverManager.registerDriver(new DriverProxy(driverInstance));
// 添加到 registeredDrivers 中
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
private void configureConnection(Connection conn) throws SQLException {
if (defaultNetworkTimeout != null) {
conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
}
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
conn.setAutoCommit(autoCommit);
}
if (defaultTransactionIsolationLevel != null) {
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}
}
doGetConnection(Properties properties)
方法:
- 初始化Driver,将其包装成DriverProxy,主要用于使用自己的Logger对象
- 获得 Connection 对象
- 配置 Connection 对象,网络超时时间、是否自动提交事务、默认的事务隔离级别
该类还包含了数据库连接的基本信息以及其他方法,我这里没有全部列出来,感兴趣的可以看下这个类😈
PooledDataSourceFactory
org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
:继承 UnpooledDataSourceFactory 类,池化的 DataSourceFactory 实现类
和 UnpooledDataSourceFactory 的区别是它创建了 PooledDataSource 对象
PooledDataSource
org.apache.ibatis.datasource.pooled.PooledDataSource
:实现 DataSource 接口,池化的 DataSource 实现类,包含数据库的连接信息以及连接池的配置信息
下面的方法都有点长,我就】就不列出来了,可以根据流程图并结合注释进行查看😈
getConnection()
方法:用于获取连接,流程图:
pushConnection
方法:"关闭"一个连接,放入空闲连接队列中或者关设置为无效状态并关闭真正的连接,在PooledConnection
的invoke
方法中,如果方法名称为close
,表示需要关闭该连接,则调用该方法,流程图:
pingConnection
方法:通过向数据库发起 poolPingQuery
语句来发起"ping"操作,以判断数据库连接是否有效,在PooledConnection
的isValid
方法中被调用,PooledDataSource的获取连接和关闭连接时都会判断该连接是否有效
forceCloseAll
方法:用于关闭所有的连接,释放资源,在定义的finalize()
方法中会被调用,即当前PooledDataSource对象被释放时
PoolState
org.apache.ibatis.datasource.pooled.PoolState
:连接池状态,记录空闲和激活的PooledConnection
集合,以及相关的数据统计
相当于一个池子,保存了空闲的或者正在被使用的连接以及一些连接池的配置信息
PooledConnection
org.apache.ibatis.datasource.pooled.PooledConnection
:实现InvocationHandler
接口,池化的 Connection 对象
连接池中保存的都是封装成PooledConnection的连接,使用JDK动态代理
的方式,调用PooledConnection的所有方法都会委托给invoke
方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// <1> 判断是否为 CLOSE 方法,则将连接放回到连接池中,避免连接被关闭
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
}
try {
// <2.1> 判断非 Object 的方法,则先检查连接是否可用
if (!Object.class.equals(method.getDeclaringClass())) {
// issue #579 toString() should never fail
// throw an SQLException instead of a Runtime
checkConnection();
}
// <2.2> 反射调用对应的方法
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
因为我们将数据库连接Connection封装成了PooledConnection,代理的时候进行了以下操作:
- 调用Connection的close方法时,有时候不用真正的关闭,需要进行一些处理
- 调用Connection其他方法之前,需要先检测该连接的可用性,然后再执行该方法
总结
实际我们不会用到Mybatis内部的数据源,都是通过Druid
或者HikariCP
等第三方组件,这里我们来看看PooledConnection使用的JDK动态代理
PooledConnection实现了InvocationHandler
接口,在invoke方法中进行了预处理,例如检查连接是否有效,然后在通过真实的Connection操作
在它的构造方法中初始化了代理类实例:
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
通过java.lang.reflect.Proxy
的newProxyInstance
方法实例化代理类对象,入参分别是类加载器,代理的接口,调用处理程序
在使用PooledConnection时,实际上MyBatis是使用内部的proxyConnection代理类的实例对象
事务模块
主要包路径:org.apache.ibatis.transaction
MyBatis的JdbcTransaction事务和纯粹的JDBC事务几乎没什么差别,仅是拓展了支持连接池的连接,事务管理也支持简单的实现,代码都比较简单,这里不展开讨论
在我们结合Spring一起使用MyBatis的时候,一般使用Spring的事务与事务管理
缓存模块
主要包路径:org.apache.ibatis.cache
MyBatis 提供了一级缓存和二级缓存,这两种缓存都依赖于该缓存模块,提供很多种的类型的缓存容器,使用了常见的装饰者模式
提供的这两种缓存性能不好且存在缺陷,很鸡肋,一般不使用,如果需要使用缓存可阅读我的另一篇文档:JetCache源码分析
org.apache.ibatis.cache.Cache
:缓存容器接口,类似于HashMap,存放缓存数据。
有以下缓存容器的实现:
org.apache.ibatis.cache.impl.PerpetualCache
:基于HashMap,永不过期org.apache.ibatis.cache.decorators.LoggingCache
:装饰Cache,提供日志打印与缓存命中统计org.apache.ibatis.cache.decorators.BlockingCache
:装饰Cache,阻塞实现,防止重复添加org.apache.ibatis.cache.decorators.SynchronizedCache
:装饰Cache,同步实现,添加synchronized
修饰符org.apache.ibatis.cache.decorators.SerializedCache
:装饰Cache,支持序列化缓存值org.apache.ibatis.cache.decorators.ScheduledCache
:装饰Cache,定时清空整个容器org.apache.ibatis.cache.decorators.FifoCache
:装饰Cache,可以设置容量,采用先进先出的淘汰机制org.apache.ibatis.cache.decorators.LruCache
:装饰Cache,容量为1024,基于 LinkedHashMap 实现淘汰机制org.apache.ibatis.cache.decorators.WeakCache
:装饰Cache,使用缓存值的强引用
org.apache.ibatis.cache.decorators.SoftCache
:装饰Cache,使用缓存值的软引用
另外也定义了缓存键org.apache.ibatis.cache.CacheKey
,可以理解成将多个对象放在一起,计算缓存键
二级缓存和Executor执行器有很大关联,在后面的文档中进行解析
类型模块
主要包路径:org.apache.ibatis.type
主要功能:
- 简化配置文件提供别名机制
- 实现 Jdbc Type 与 Java Type 之间的转换
这个模块涉及到很多类,因为不同类型需要有对应的类型处理器,逻辑都差不多
主要查看以下几个类:
org.apache.ibatis.type.TypeHandler
:类型处理器接口org.apache.ibatis.type.BaseTypeHandler
:实现 TypeHandler 接口,继承 TypeReference 抽象类,TypeHandler 基础抽象类org.apache.ibatis.type.IntegerTypeHandler
:继承 BaseTypeHandler 抽象类,Integer 类型的 TypeHandler 实现类org.apache.ibatis.type.UnknownTypeHandler
:继承 BaseTypeHandler 抽象类,未知的 TypeHandler 实现类,通过获取对应的 TypeHandler ,进行处理org.apache.ibatis.type.TypeHandlerRegistry
:TypeHandler 注册表,相当于管理 TypeHandler 的容器,从其中能获取到对应的 TypeHandlerorg.apache.ibatis.type.TypeAliasRegistry
:类型与别名的注册表,通过别名我们可以在 XML 映射文件中配置resultType
和parameterType
属性时,直接配置为别名而不用写全类名
TypeHandler
org.apache.ibatis.type.TypeHandler
:类型处理器接口,代码如下:
public interface TypeHandler<T> {
/**
* 设置 PreparedStatement 的指定参数
*
* Java Type => JDBC Type
*
* @param ps PreparedStatement 对象
* @param i 参数占位符的位置
* @param parameter 参数
* @param jdbcType JDBC 类型
* @throws SQLException 当发生 SQL 异常时
*/
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* 获得 ResultSet 的指定字段的值
*
* JDBC Type => Java Type
*
* @param rs ResultSet 对象
* @param columnName 字段名
* @return 值
* @throws SQLException 当发生 SQL 异常时
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* 获得 ResultSet 的指定字段的值
*
* JDBC Type => Java Type
*
* @param rs ResultSet 对象
* @param columnIndex 字段位置
* @return 值
* @throws SQLException 当发生 SQL 异常时
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
/**
* 获得 CallableStatement 的指定字段的值
*
* JDBC Type => Java Type
*
* @param cs CallableStatement 对象,支持调用存储过程
* @param columnIndex 字段位置
* @return 值
* @throws SQLException
*/
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
#setParameter(...)
方法,是 Java Type => Jdbc Type 的过程
#getResult(...)
方法,是 Jdbc Type => Java Type 的过程
BaseTypeHandler
org.apache.ibatis.type.BaseTypeHandler
:实现 TypeHandler 接口,继承 TypeReference 抽象类,TypeHandler 基础抽象类
在方法的实现中捕获异常,内部调用抽象方,交由子类去实现
IntegerTypeHandler
org.apache.ibatis.type.IntegerTypeHandler
:继承 BaseTypeHandler 抽象类,Integer 类型的 TypeHandler 实现类
往java.sql.PreparedStatement
设置参数或者从java.sql.ResultSet
获取结果,逻辑不复杂,代码如下:
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
throws SQLException {
// 直接设置参数即可
ps.setInt(i, parameter);
}
@Override
public Integer getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 获得字段的值
int result = rs.getInt(columnName);
// 先通过 rs 判断是否空,如果是空,则返回 null ,否则返回 result
return result == 0 && rs.wasNull() ? null : result;
}
@Override
public Integer getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int result = rs.getInt(columnIndex);
return result == 0 && rs.wasNull() ? null : result;
}
@Override
public Integer getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int result = cs.getInt(columnIndex);
return result == 0 && cs.wasNull() ? null : result;
}
}
UnknownTypeHandler
org.apache.ibatis.type.UnknownTypeHandler
:继承 BaseTypeHandler 抽象类,未知的 TypeHandler 实现类,通过获取对应的 TypeHandler ,进行处理
内部有一个TypeHandlerRegistry
对象,TypeHandler 注册表,保存了Java Type、JDBC Type与TypeHandler 之间的映射关系
代码如下:
public class UnknownTypeHandler extends BaseTypeHandler<Object> {
/**
* ObjectTypeHandler 单例
*/
private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();
/**
* TypeHandler 注册表
*/
private TypeHandlerRegistry typeHandlerRegistry;
private TypeHandler<?> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
TypeHandler<?> handler;
// 参数为空,返回 OBJECT_TYPE_HANDLER
if (parameter == null) {
handler = OBJECT_TYPE_HANDLER;
} else { // 参数非空,使用参数类型获得对应的 TypeHandler
handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
// check if handler is null (issue #270)
// 获取不到,则使用 OBJECT_TYPE_HANDLER
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = OBJECT_TYPE_HANDLER;
}
}
return handler;
}
private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) {
TypeHandler<?> handler = null;
// 获得 JDBC Type 类型
JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
// 获得 Java Type 类型
Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
//获得对应的 TypeHandler 对象
if (javaType != null && jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
} else if (javaType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType);
} else if (jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(jdbcType);
}
return handler;
}
}
上面我只是列出其两个关键方法:
resolveTypeHandler(ResultSet rs, String column)
:根据参数类型获取对应的TypeHandler类型处理器
resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex)
:通过ResultSetMetaData获取某列的Jdbc Type和Java Type,然后获取对应的TypeHandler类型处理器
TypeHandlerRegistry
org.apache.ibatis.type.TypeHandlerRegistry
:TypeHandler 注册表,相当于管理 TypeHandler 的容器,从其中能获取到对应的 TypeHandler 类型处理器,部分代码如下:
public final class TypeHandlerRegistry {
/**
* JDBC Type 和 {@link TypeHandler} 的映射
*
* {@link #register(JdbcType, TypeHandler)}
*/
private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
/**
* {@link TypeHandler} 的映射
*
* KEY1:Java Type
* VALUE1:{@link jdbcTypeHandlerMap} 对象,例如Date对应多种TypeHandler,所以采用Map
* KEY2:JDBC Type
* VALUE2:{@link TypeHandler} 对象
*/
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
/**
* {@link UnknownTypeHandler} 对象
*/
private final TypeHandler<Object> unknownTypeHandler = new UnknownTypeHandler(this);
/**
* 所有 TypeHandler 的“集合”
*
* KEY:{@link TypeHandler#getClass()}
* VALUE:{@link TypeHandler} 对象
*/
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
/**
* 空 TypeHandler 集合的标识,即使 {@link #TYPE_HANDLER_MAP} 中,某个 KEY1 对应的 Map<JdbcType, TypeHandler<?>> 为空。
*
* @see #getJdbcHandlerMap(Type)
*/
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
/**
* 默认的枚举类型的 TypeHandler 对象
*/
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new StringTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
// ... 省略 ...
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
public void register(String packageName) {
// 扫描指定包下的所有 TypeHandler 类
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
// 遍历 TypeHandler 数组,发起注册
for (Class<?> type : handlerSet) {
// Ignore inner classes and interfaces (including package-info.java) and
// abstract classes
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
register(type);
}
}
}
}
在构造函数中可以看到会将 Java 的常用类型、JDBC 的类型与对应的 TypeHandler 类型处理器进行映射然后保存起来
提供了register(String packageName)
方法,可以注册指定包路径下的 TypeHandler 类型处理器
TypeAliasRegistry
org.apache.ibatis.type.TypeAliasRegistry
:类型与别名的注册表通过别名,主要代码如下:
public class TypeAliasRegistry {
/**
* 类型与别名的映射
*/
private final Map<String, Class<?>> typeAliases = new HashMap<>();
/**
* 初始化默认的类型与别名
*
* 另外,在 {@link org.apache.ibatis.session.Configuration} 构造方法中,也有默认的注册
*/
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte[]", Byte[].class);
registerAlias("_byte", byte.class);
registerAlias("_byte[]", byte[].class);
registerAlias("date", Date.class);
registerAlias("date[]", Date[].class);
registerAlias("map", Map.class);
// ... 省略 ...
registerAlias("ResultSet", ResultSet.class);
}
@SuppressWarnings("unchecked")
// throws class cast exception as well if types cannot be assigned
public <T> Class<T> resolveAlias(String string) {
// 获得别名对应的类型
try {
if (string == null) {
return null;
}
// issue #748
// <1> 转换成小写
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
// <2.1> 首先,从 TYPE_ALIASES 中获取
if (typeAliases.containsKey(key)) {
value = (Class<T>) typeAliases.get(key);
} else { // <2.2> 其次,直接获得对应类
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
/**
* 注册指定包下的别名与类的映射
*
* @param packageName 指定包
*/
public void registerAliases(String packageName) {
registerAliases(packageName, Object.class);
}
/**
* 注册指定包下的别名与类的映射。另外,要求类必须是 {@param superType} 类型(包括子类)。
*
* @param packageName 指定包
* @param superType 指定父类
*/
public void registerAliases(String packageName, Class<?> superType) {
// 获得指定包下的类门
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
// 遍历,逐个注册类型与别名的注册表
for (Class<?> type : typeSet) {
// Ignore inner classes and interfaces (including package-info.java)
// Skip also inner classes. See issue #6
if (!type.isAnonymousClass() // 排除匿名类
&& !type.isInterface() // 排除接口
&& !type.isMemberClass()) { // 排除内部类
registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
// <1> 默认为,简单类名
String alias = type.getSimpleName();
// <2> 如果有注解,使用注册上的名字
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
// <3> 注册类型与别名的注册表
registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
// <1> 转换成小写
// 将别名转换成**小写**。这样的话,无论我们在 Mapper XML 中,写 `String` 还是 `string` 甚至是 `STRING` ,都是对应的 String 类型
String key = alias.toLowerCase(Locale.ENGLISH);
// <2> 冲突,抛出 TypeException 异常
// 如果已经注册,并且类型不一致,说明有冲突,抛出 TypeException 异常
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '"
+ typeAliases.get(key).getName() + "'.");
}
typeAliases.put(key, value);
}
public void registerAlias(String alias, String value) {
try {
registerAlias(alias, Resources.classForName(value));
} catch (ClassNotFoundException e) {
throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + e, e);
}
}
/**
* @since 3.2.2
*/
public Map<String, Class<?>> getTypeAliases() {
return Collections.unmodifiableMap(typeAliases);
}
}
在构造函数中可以看到,将 Java 常用类型的别名添加到Map<String, Class<?>> typeAliases
对象中
registerAliases(String packageName)
方法,注册指定包下别名与Class对象的映射
- 如果使用了
@Alias
注解,则获取其value作为别名,否则的话使用类的简单类名 - 别名都会转换成小写,这样的话,无论我们在 Mapper XML 中,写
String
还是string
甚至是STRING
,都是对应的String类型
resolveAlias(String string)
方法,根据参数名称获取其Class对象
- 如果参数名称在
typeAliases
中存在,则直接返回对应的Class对象 - 否则根据该参数名称调用
Class.forName
方法创建一个Class对象
总结
通过配置别名的方式(指定包路径或者实体类添加@Alias
注解)可以简化我们的XML映射文件的配置
在别名注册中心已经提供了Java常用类型与Class对象的映射,且映射的key值全部转换成小写了,更加便于我们编写XML映射文件
提供多种类型处理器,实现了Jdbc Type与Java Type之间的转换,在TypeHandlerRegistry注册表中已经初始化好了许多需要用到的类型处理器,便于其他模块进行解析
IO模块
主要包路径:org.apache.ibatis.io
主要功能:加载类文件以及其他资源文件,例如加载某个包名下面所有的Class对象
主要看以下几个类:
org.apache.ibatis.io.ClassLoaderWrapper
:ClassLoader类加载器的包装器org.apache.ibatis.io.Resources
:Resources工具类,通过ClassLoaderWrapper获取资源org.apache.ibatis.io.ResolverUtil
:解析器工具类,用于获得指定目录符合条件的Class对象org.apache.ibatis.io.VFS
:虚拟文件系统(Virtual File System)抽象类,用来查找指定路径下的文件们org.apache.ibatis.io.DefaultVFS
:继承VFS抽象类,默认的VFS实现类
双亲委派机制
我们将.java文件编译成.class文件后,需要通过ClassLoader类加载器将.class文件将其转换成Class对象,然后"加载"到JVM中,这样我们就可以通过该Class对象创建实例和初始化等操作,其中ClassLoader类加载器使用了双亲委派机制来加载文件
类加载器:
- 启动类加载器:为null,JVM启动时创建,由HotSpot实现,负责加载jre/lib/rt.jar包下的核心类
- 扩展类加载器:定义在sun.misc.Launcher的一个内部类,sun.misc.Launcher$ExtClassLoader,负责加载jre/lib/ext/*.jar包下的扩展类
- 应用类加载器:定义在sun.misc.Launcher的一个内部类,sun.misc.Launcher$AppClassLoader,负责加载ClassPath路径下面的类
- 自定义类加载器:用户自定义的类加载器,可以通过重写findClass()方法进行加载类
注意:除了启动类加载器外,所有的类加载器都是ClassLoader的子类,原因在于启动类加载器是由C语言而不是Java实现的,ClassLoader中有两个重要的方法: loadClass(String name,boolean resolve)和findClass(String name), loadClass方法实现双亲委派机制子一般不进行重写,各子类加载器通过重写findClass方法实现自己的类加载业务。
可以看到除了除了启动类加载器外,每个类加载器都有父加载器,当一个类加载器加载一个类时,首先会把加载动作委派给他的父加载器,如果父加载器无法完成这个加载动作时才由该类加载器进行加载。由于类加载器会向上传递加载请求,所以一个类加载时,首先尝试加载它的肯定是启动类加载器(逐级向上传递请求,直到启动类加载器,它没有父加载器),之后根据是否能加载的结果逐级让子类加载器尝试加载,直到加载成功。
意义:防止加载同一个.class文件、保证核心API不会被篡改
ClassLoaderWrapper
org.apache.ibatis.io.ClassLoaderWrapper
:ClassLoader类加载器的包装器
在构造器中会获取系统的构造器,也就是应用类加载器
提供的功能:
- 获取一个资源返回URL
- 获取一个资源返回InputStream
- 获取指定类名的Class对象
说明:都是通过ClassLoader类加载器加载资源的,可以通过多个ClassLoader进行加载,直到有一个成功则返回资源
Resources
org.apache.ibatis.io.Resources
:Resources工具类,通过ClassLoaderWrapper获取资源
相当于对ClassLoaderWrapper在进行一层包装,如果没有获取到资源则可能抛出异常,还提供更多的方法,例如获取资源返回Properties对象,获取资源返回Reader对象,获取资源返回File对象
ResolverUtil
org.apache.ibatis.io.ResolverUtil
:解析器工具类,用于获得指定目录符合条件的Class对象
在通过包名加载Mapper接口,需要使用别名的类,TypeHandler类型处理器类需要使用该工具类
内部定义了一个接口和两个实现类:
public class ResolverUtil<T> {
public interface Test {
boolean matches(Class<?> type);
}
/**
* 判断是匹配指定类
*/
public static class IsA implements Test {
private Class<?> parent;
public IsA(Class<?> parentType) {
this.parent = parentType;
}
/**
* Returns true if type is assignable to the parent type supplied in the constructor.
*/
@Override
public boolean matches(Class<?> type) {
return type != null && parent.isAssignableFrom(type);
}
@Override
public String toString() {
return "is assignable to " + parent.getSimpleName();
}
}
/**
* 判断是否匹配指定注解
*/
public static class AnnotatedWith implements Test {
private Class<? extends Annotation> annotation;
public AnnotatedWith(Class<? extends Annotation> annotation) {
this.annotation = annotation;
}
/**
* Returns true if the type is annotated with the class provided to the constructor.
*/
@Override
public boolean matches(Class<?> type) {
return type != null && type.isAnnotationPresent(annotation);
}
@Override
public String toString() {
return "annotated with @" + annotation.getSimpleName();
}
}
}
上面两个内部类很简单,用于判断是否匹配指定类或者指定注解
我们来看看主要的几个方法:
public class ResolverUtil<T> {
private Set<Class<? extends T>> matches = new HashSet<>();
public Set<Class<? extends T>> getClasses() {
return matches;
}
/**
* 获取包路径下 parent 的子类
*/
public ResolverUtil<T> findImplementations(Class<?> parent, String... packageNames) {
if (packageNames == null) {
return this;
}
Test test = new IsA(parent);
for (String pkg : packageNames) {
find(test, pkg);
}
return this;
}
/**
* 获取包路径下 annotation 的子注解
*/
public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) {
if (packageNames == null) {
return this;
}
Test test = new AnnotatedWith(annotation);
for (String pkg : packageNames) {
find(test, pkg);
}
return this;
}
/**
* 获得指定包下,符合条件的类
*/
public ResolverUtil<T> find(Test test, String packageName) {
// <1> 获得包的路径
String path = getPackagePath(packageName);
try {
// <2> 获得路径下的所有文件
List<String> children = VFS.getInstance().list(path);
// <3> 遍历
for (String child : children) {
// 是 Java Class
if (child.endsWith(".class")) {
// 如果匹配,则添加到结果集
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
protected String getPackagePath(String packageName) {
return packageName == null ? null : packageName.replace('.', '/');
}
@SuppressWarnings("unchecked")
protected void addIfMatching(Test test, String fqn) {
try {
// 获得全类名
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
if (log.isDebugEnabled()) {
log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
}
// 加载类
Class<?> type = loader.loadClass(externalName);
// 判断是否匹配
if (test.matches(type)) {
matches.add((Class<T>) type);
}
} catch (Throwable t) {
log.warn("Could not examine class '" + fqn + "'" + " due to a " + t.getClass().getName() + " with message: "
+ t.getMessage());
}
}
}
如果我们需要获取某个包路径下的所有类,则可以设置父类为java.lang.Object,调用findImplementations
方法即可
再来看到find(Test test, String packageName)
方法,需要调用VFS
的list
方法获取到所有的文件,然后将匹配的.class文件通过ClassLoader加载成Class对象
VFS
org.apache.ibatis.io.VFS
:虚拟文件系统(Virtual File System)抽象类,用来查找指定路径下的文件们
提供几个反射的相关方法,list
方法则交由子类实现
默认实现类有JBoss6VFS和DefaultVFS
,当然可以自定义 VFS 实现类,我们来看看如何返回VFS实例的,代码如下:
public abstract class VFS {
public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class }; // 内置的 VFS 实现类的数组
public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>(); // 自定义的 VFS 实现类的数组
public static VFS getInstance() {
return VFSHolder.INSTANCE;
}
private static class VFSHolder {
static final VFS INSTANCE = createVFS();
@SuppressWarnings("unchecked")
static VFS createVFS() {
// Try the user implementations first, then the built-ins
List<Class<? extends VFS>> impls = new ArrayList<>();
impls.addAll(USER_IMPLEMENTATIONS);
impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));
// Try each implementation class until a valid one is found
VFS vfs = null;
// 创建 VFS 对象,选择最后一个符合的
for (int i = 0; vfs == null || !vfs.isValid(); i++) {
Class<? extends VFS> impl = impls.get(i);
try {
vfs = impl.getDeclaredConstructor().newInstance();
if (!vfs.isValid()) {
if (log.isDebugEnabled()) {
log.debug("VFS implementation " + impl.getName() + " is not valid in this environment.");
}
}
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException
| InvocationTargetException e) {
log.error("Failed to instantiate " + impl, e);
return null;
}
}
if (log.isDebugEnabled()) {
log.debug("Using VFS adapter " + vfs.getClass().getName());
}
return vfs;
}
}
}
可以看到获取的VFS实例会进入createVFS()
方法,从提供的几个VFS实现类中选一个符合的,最后可以看到会返回最后一个符合的,也就是定义的DefaultVFS
DefaultVFS
org.apache.ibatis.io.DefaultVFS
:继承VFS抽象类,默认的VFS实现类
DefaultVFS实现类VFS的list(URL url, String path)
方法,在ResolverUtil
的find
方法中会被调用,由于代码冗长,这里就不列出来了,请自行查看该类😈😈😈
大致逻辑如下:
- 如果url指向一个JAR Resource,那么从这个JAR Resource中获取path路径下的文件名称
- 否则获取该url中path下面所有的文件,会不断的递归获取到所有文件(包含我们需要的.class文件),最后将获取到的所有文件名称返回
这样我们就可以在ResolverUtil
将我们需要的.class文件加载成对应的Class对象
总结
了解Java中类加载的双亲委派机制
Mybatis中需要加载类是通过该模块中的ResolverUtil来实现的,大家可以先顺着org.apache.ibatis.binding.MapperRegistry.addMappers(String packageName)
扫描Mapper接口这个方法看看整个的解析过程
日志模块
主要包路径:org.apache.ibatis.logging
主要功能:集成第三方日志框架,Debug模块输出JDBC操作日志
LogFactory
org.apache.ibatis.logging.LogFactory
:日志工厂
主要代码如下:
public final class LogFactory {
public static final String MARKER = "MYBATIS";
/**
* 使用的 Log 的构造方法
*/
private static Constructor<? extends Log> logConstructor;
static {
// <1> 逐个尝试,判断使用哪个 Log 的实现类,即初始化 logConstructor 属性
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}
private LogFactory() {
// disable construction
}
public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
}
public static Log getLog(String logger) {
try {
return logConstructor.newInstance(logger);
} catch (Throwable t) {
throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
}
}
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
setImplementation(clazz);
}
public static synchronized void useSlf4jLogging() {
setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
}
public static synchronized void useCommonsLogging() {
setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
}
public static synchronized void useLog4JLogging() {
setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
}
public static synchronized void useLog4J2Logging() {
setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
}
private static void tryImplementation(Runnable runnable) {
if (logConstructor == null) {
try {
runnable.run();
} catch (Throwable t) {
// ignore
}
}
}
private static void setImplementation(Class<? extends Log> implClass) {
try {
// 获得参数为 String 的构造方法
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
// 创建 Log 对象
Log log = candidate.newInstance(LogFactory.class.getName());
if (log.isDebugEnabled()) {
log.debug("Logging initialized using '" + implClass + "' adapter.");
}
// 创建成功,意味着可以使用,设置为 logConstructor
logConstructor = candidate;
} catch (Throwable t) {
throw new LogException("Error setting Log implementation. Cause: " + t, t);
}
}
}
- 在静态代码块中会逐个尝试,判断使用哪个
Log
的实现类,即初始化logConstructor
日志构造器 - 在
getLog(String logger)
方法获取日志的实例对象时,通过logConstructor
创建一个实例出来,这样就完成了日志的适配
BaseJdbcLogger
在org.apache.ibatis.logging.jdbc包路径下有几个BaseJdbcLogger类,用于DEBUG模式下打印JDBC操作日志
这里也使用了JDK动态代理,对JDBC接口进行增强,打印执行日志,和数据源模块类似
可以在org.apache.ibatis.executor.BaseExecutor
的getConnection
方法中看到,如果开启了DEBUG模式,则创建Connection的动态代理对象,可以顺着下去看
注解模块
在实际使用MyBatis过程中,我们大部分都是使用XML的方式,这样我们便于维护
当然MyBatis也提供了许多注解,让我们在Mapper接口上面可以通过注解编写SQL
主要包路径:org.apache.ibatis.annotations
@Param
用于设置Mapper接口中的方法的参数名称
如果我们Mapper接口的方法中有多个入参,我们需要通过该注解来为每个参数设置一个名称
在反射模块的ParamNameResolver
工具类中有讲到会通过该注解设置参数名称
@Mapper
用于标记接口为Mapper接口
在Spring Boot项目中通过mybatis-spring-boot-starter
使用MyBatis时,如果你没有通过mybatis-spring
子项目中的三种方式(配置MapperScannerConfigurer扫描器、@MapperScan注解或者<mybatis:scan />标签)配置Mapper接口包路径,那么在mybatis-spring-boot
中则会扫描Spring Boot项目设置的基础包路径,如果设置了@Mapper
注解,则会当成Mapper接口进行解析,后面的文档中会讲到😈
其他注解
@Select、@Insert、@Update、@Delete,CURD注解
Binding模块
主要包路径:org.apache.ibatis.binding
在使用MyBatis时,我们通常定义Mapper接口,然后在对应的XML映射文件中编写SQL语句,那么当我们调用接口的方法时,为什么可以执行对应的SQL语句?通过该模块我们可以对它们是如何关联起来的有个大致的了解
Mybatis会自动为Mapper接口创建一个动态代理实例,通过该动态代理对象的实例进行相关操作
主要涉及到以下几个类:
org.apache.ibatis.binding.MapperRegistry
:Mapper接口注册中心,将Mapper接口与其动态代理对象工厂进行保存org.apache.ibatis.binding.MapperProxyFactory
:Mapper接口的动态代理对象工厂,用于生产Mapper接口的动态代理对象org.apache.ibatis.binding.MapperProxy
:Mapper接口的动态代理对象,使用JDK动态代理,实现了java.lang.reflect.InvocationHandler
接口org.apache.ibatis.binding.MapperMethod
:Mapper接口中定义的方法对应的Mapper方法,通过它来执行SQL
MapperRegistry
org.apache.ibatis.binding.MapperRegistry
:Mapper接口注册中心,将Mapper接口与其MapperProxyFactory
动态代理对象工厂进行保存
主要代码如下:
public class MapperRegistry {
/**
* MyBatis Configuration 对象
*/
private final Configuration config;
/**
* MapperProxyFactory 的映射
*
* KEY:Mapper 接口
*/
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// <1> 获得 MapperProxyFactory 对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 不存在,则抛出 BindingException 异常
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 创建 Mapper Proxy 对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
public <T> void addMapper(Class<T> type) {
// <1> 判断,必须是接口。
if (type.isInterface()) {
// <2> 已经添加过,则抛出 BindingException 异常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// <3> 将Mapper接口对应的代理工厂添加到 knownMappers 中
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the mapper parser.
// If the type is already known, it won't try.
// <4> 解析 Mapper 的注解配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析 Mapper 接口上面的注解和 Mapper 接口对应的 XML 文件
parser.parse();
// <5> 标记加载完成
loadCompleted = true;
} finally {
// <6> 若加载未完成,从 knownMappers 中移除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
/**
* @since 3.2.2
*/
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
}
/**
* 用于扫描指定包中的Mapper接口,并与XML文件进行绑定
* @since 3.2.2
*/
public void addMappers(String packageName, Class<?> superType) {
// <1> 扫描指定包下的指定类
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
// <2> 遍历,添加到 knownMappers 中
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
}
addMappers(String packageName, Class<?> superType)
方法:
- 获取该包路径下的Mapper接口Class对象,然后调用
addMapper(Class<T> type)
进行解析,这里使用了ResolverUtil工具类,获取到该包路径下所有匹配Object.class
的类,这个工具类在IO模块中已经讲过
addMapper(Class<T> type)
方法:
- 创建Mapper接口对应的动态代理对象工厂MapperProxyFactory,添加到
knownMappers
中 - 通过
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder
对该接口进行解析,解析对应的XML映射文件(接口名称+'.xml'文件)
整个的解析过程比较复杂,在后续的《Mybatis的初始化》文档中进行分析
getMapper(Class<T> type, SqlSession sqlSession)
方法:根据Mapper接口的Class对象和SqlSession对象创建一个动态代理对象
- 根据Mapper接口的Class对象获取对应的MapperProxyFactory工厂
- 通过MapperProxyFactory工厂创建一个动态代理对象实例
MapperProxyFactory
org.apache.ibatis.binding.MapperProxyFactory
:Mapper接口的动态代理对象工厂,用于生产Mapper接口动态代理对象的实例,代码如下:
public class MapperProxyFactory<T> {
/**
* Mapper 接口
*/
private final Class<T> mapperInterface;
/**
* 方法与 MapperMethod 的映射
*/
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
newInstance(SqlSession sqlSession)
方法:创建Mapper接口对应的动态代理对象的实例,可以看到是通过MapperProxy的构造方法创建了一个动态代理对象的
MapperProxy
org.apache.ibatis.binding.MapperProxy
:Mapper接口的动态代理对象,使用JDK动态代理,实现了java.lang.reflect.InvocationHandler
接口,部分代码如下:
public class MapperProxy<T> implements InvocationHandler, Serializable {
/**
* SqlSession 对象
*/
private final SqlSession sqlSession;
/**
* Mapper 接口
*/
private final Class<T> mapperInterface;
/**
* 方法与 MapperMethod 的映射
*
* 从 {@link MapperProxyFactory#methodCache} 传递过来
*/
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// <1> 如果是 Object 定义的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) { // 是否有 default 修饰的方法
// 针对Java7以上版本对动态类型语言的支持
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// <2.1> 获得 MapperMethod 对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
// <2.2> 执行 MapperMethod 方法
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
// 从methodCache缓存中获取MapperMethod方法,如果为空则创建一下新的并添加至缓存中
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
}
在这个Mapper接口的动态代理对象中,覆盖的invoke
方法处理逻辑:
- 如果是Object.class定义的方法,则直接调用
- 如果该方法有默认实现则调用其默认实现,在jdk8中支持接口中的方法可以通过
default
修饰符定义他的默认实现 - 否则的话,获取该Mapper接口中的该方法对应的MapperMethod对象
- 工厂中定义了一个
ConcurrentHashMap<Method, MapperMethod> methodCache
对象,用于存储该Mapper接口中方法对应的MapperMethod对象 - 根据方法获取
MapperMethod
对象,如果没有则通过MapperMethod
的构造函数创建一个实例并添加到缓存中
- 通过Mapper接口中该方法对应的
MapperMethod
对象,执行相应的SQL操作
MapperMethod
org.apache.ibatis.binding.MapperMethod
:Mapper接口中定义的方法对应的Mapper方法,通过它来执行SQL,构造方法:
public class MapperMethod {
/**
* 该方法对应的 SQL 的唯一编号与类型
*/
private final SqlCommand command;
/**
* 该方法的签名,包含入参和出参的相关信息
*/
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
}
构造方法初始化了两个对象,分别为SqlCommand
和MethodSignature
,都是其内部类
还定义了一个execute(SqlSession sqlSession, Object[] args)
方法,具体的SQL语句执行逻辑,在后续模块的文档中进行分析
SqlCommand
public class MapperMethod {
public static class SqlCommand {
/**
* SQL的唯一编号:namespace+id(Mapper接口名称+'.'+方法名称),{# MappedStatement#id}
*/
private final String name;
/**
* SQL 命令类型 {# MappedStatement#sqlCommandType}
*/
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 获取该方法对应的 MappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
if (ms == null) {
// 如果有 @Flush 注解,则标记为 FLUSH 类型
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException(
"Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// 生成 MappedStatement 唯一编号:接口名称+'.'+方法名称
String statementId = mapperInterface.getName() + "." + methodName;
// 在全局对象 Configuration 中获取对应的 MappedStatement
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
// 如果方法就是定义在该接口中,又没找到则直接返回 null
return null;
}
// 遍历父接口,获取对应的 MappedStatement
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
}
在构造方法中就是通过statementId(Mapper接口名称+'.'+方法名称
)获取到对应的MappedStatement
对象(在XML映射文件中该方法对应的SQL语句生成的MappedStatement对象),在后续的MyBatis的初始化文档中会分析该对象是如何创建的
所以我们定义的XML文件的
namespace
通常是接口名称,<select />
等节点定义的id就是方法名称,这样才能对应起来同一个Mapper接口中不能有重载方法也是这个道理,两个方法对应同一个statementId就出错了
获取到了MappedStatement对象则设置name
为它的id(Mapper接口名称+'.'+方法名称
),type
为它的SqlCommandType(UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
)
MethodSignature
public class MapperMethod {
public static class MethodSignature {
/**
* 返回数据是否包含多个
*/
private final boolean returnsMany;
/**
* 返回类型是否为Map的子类,并且该方法上面使用了 @MapKey 注解
*/
private final boolean returnsMap;
/**
* 返回类型是否为 void
*/
private final boolean returnsVoid;
/**
* 返回类型是否为 Cursor
*/
private final boolean returnsCursor;
/**
* 返回类型是否为 Optional
*/
private final boolean returnsOptional;
/**
* 返回类型
*/
private final Class<?> returnType;
/**
* 方法上 @MapKey 注解定义的值
*/
private final String mapKey;
/**
* 用来标记该方法参数列表中 ResultHandler 类型参数得位置
*/
private final Integer resultHandlerIndex;
/**
* 用来标记该方法参数列表中 RowBounds 类型参数得位置
*/
private final Integer rowBoundsIndex;
/**
* ParamNameResolver 对象,主要用于解析 @Param 注解定义的参数,参数值与参数得映射等
*/
private final ParamNameResolver paramNameResolver;
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
// 获取该方法的返回类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) { // 泛型类型
// 获取该参数化类型的实际类型
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
// 是否为无返回结果
this.returnsVoid = void.class.equals(this.returnType);
// 返回类型是否为集合或者数组类型
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
// 返回类型是否为游标类型
this.returnsCursor = Cursor.class.equals(this.returnType);
// 返回结果是否则 Optional 类型
this.returnsOptional = Optional.class.equals(this.returnType);
// 解析方法上面的 @MapKey 注解
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// 方法参数类型为 RowBounds 的位置
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// 方法参数类型为 ResultHandler 的位置
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
/*
* 解析该方法参数名称生成参数位置与参数名称的映射
* @Param 注解则取其值作为参数名称,否则取其真实的参数名称,在没有则为参数位置
*/
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
/**
* 根据入参返回参数名称与入参的映射
*
* @param args 入参
* @return 参数名称与入参的映射
*/
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
Integer index = null;
final Class<?>[] argTypes = method.getParameterTypes();
for (int i = 0; i < argTypes.length; i++) {
if (paramType.isAssignableFrom(argTypes[i])) {
if (index == null) {
index = i;
} else {
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
}
}
}
return index;
}
}
}
在构造函数中解析Mapper接口中该方法的相关信息并设置到对应的属性中,会解析出以下信息:
- 返回值类型
- @MapKey注解,使用示例参考:@MapKey注解的使用
- 参数类型为 RowBounds 的位置
- 参数类型为 ResultHandler 的位置
- 生成参数名称解析器
ParamNameResolver
,在反射模块有讲到,用于根据参数值返回参数名称与参数值的映射关系
总结
每一个Mapper接口会有一个MapperProxyFactory
动态代理对象工厂,保存于MapperRegistry
注册中心
调用接口的方法时,会进入MapperProxy
动态代理对象中,然后通过该方法对应的MapperMethod
方法执行SQL语句的相关操作
疑问:
- Mapper接口的动态代理对象会在哪里创建?
通过DefaultSqlSession的getMapper(Class<T> type)
方法可以获取到Mapper接口的动态代理对象- Mapper接口是怎么作为对象注入到其他Spring Bean中?
通过实现BeanDefinitionRegistryPostProcessor接口,修改Mapper接口的BeanDefiniton对象,修改为MapperFactoryBean类型,在FactoryBean中的getObject()方法中获取到对应的动态代理对象