mybatis源码学习------binding模块(上)
简介
Mybatis的Binding模块用于实现Mapper接口与Mapper.xml绑定,当用于定义了AuthorMapper.java和AuthorMapper.xml后,mybatis需要将AuthorMapper.java中定义的方法与AuthorMapper.xml中定义的sql语句关联起来,并封装到指定的数据结构中便于后续上层应用的调用。如果在绑定的过程中发现存在无法关联的sql语句,则mybatis会在初始化时抛出异常,而不是等到用户实际调用的时候再抛出异常。
问题: 平时的业务代码调用的都是xxxMapper.java中定义的接口方法,且并没有为该方法提供实现,mybatis是如何通过xxxMapper接口找到对应的sql语句的?由是如何将业务代码中传入的实参传给sql语句的?
本篇文章将会分析mybatis的org.apache.ibatis.binding包中的代码,并试图回答上面的问题。
binding模块中的类如下图所示;
类图如下所示:
接下来按照类图从上到下对逐个类进行分析学习
MapperRegistry
属性
MapperRegistry是Mapper接口和MapperProxyFactory的注册表,MapperRegistry类定义了两个属性:
- Configuration config:保存着mybatis全局的配置信息
- Map<Class, MapperProxyFactory> knownMappers :维护Mapper接口与MapperProxyFactory之间的对应关系
既然是注册表就一定少不了增删改查的方法,该类中的核心方法为addMapper(class type),接下来重点分析,其余方法直接看注释就可以。
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//从knownMappers中查找指定type对应的MapperProxyFactory实例
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//调用MapperProxyFactory的方法创建Mapper的代理对象
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);
}
/**
* Gets the mappers.
*
* @return the mappers
* @since 3.2.2
*/
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
}
/**
* Adds the mappers.
*
* @param packageName
* the package name
* @param superType
* the super type
* @since 3.2.2
*
* 批量添加Mapper
*/
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
/**
* Adds the mappers.
*
* @param packageName
* the package name
* @since 3.2.2
*
* 将packageName包下的所有Mapper添加到knownMappers中
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
}
addMapper(Class)
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {//mapper必须是接口
if (hasMapper(type)) {//防止重复添加
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//标记1
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.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperProxyFactory
MapperProxyFactory类主要用于创建实现了mapperInterface接口的MapperProxy的对象。
public class MapperProxyFactory<T> {
//被代理的类
private final Class<T> mapperInterface;
//key为mapperInterface类中定义的方法Method对象,value为与之对应的MapperMethod对象
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
//调用重载方法,创建mapperInterface的代理类
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
newInstance方法最终创建了一个实现了mapperInterface接口的代理对象,传入的是MapperProxy实例,接下来分析MapperProxy类的实现。
MapperProxy
从下面MapperProxy类的定义可以看到,MapperProxy是一个实现了InvocationHandler接口的类,这是JDK动态代理的规范,所以MapperProxy类实现的Invoke方法将会是整个类的核心逻辑。
public class MapperProxy<T> implements InvocationHandler, Serializable {......}
属性
private static final long serialVersionUID = -4724728412955527868L;
//定义了允许访问何种访问权限的方法
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
private static final Constructor<Lookup> lookupConstructor;//方法句柄对象的构造函数
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;//关联的SqlSession对象
private final Class<T> mapperInterface;//被代理的接口
private final Map<Method, MapperMethodInvoker> methodCache;//用于维护mapperInterface中定义的方法和MapperMethod对象的映射关系
静态代码块
静态代码块中的主要逻辑是找到MethodHandle的构造函数对象,便于后续创建MethodHandle对象,但是考虑到JDK8与JDK9版本对MethodHandle实现的差异,其余代码都是为了能够兼容版本差异。
关于MethodHandle,简单来说就是JDK7发布的一套和反射有关的API,可以看做是反射2.0,感兴趣的话可以查看这篇文章进行学习https://blog.csdn.net/qq_31865983/article/details/102759032?utm_medium=distribute.pc_relevant.none-task-blog-title-2&spm=1001.2101.3001.4242
static {
//JDK 9 的方法
Method privateLookupIn;
try {
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (NoSuchMethodException e) {
privateLookupIn = null;
}
privateLookupInMethod = privateLookupIn;
//JDK 8 的方法获取MethodHandles的构造函数
Constructor<Lookup> lookup = null;
if (privateLookupInMethod == null) {
// JDK 1.8
try {
//获取指定参数的构造器
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookup.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
e);
} catch (Exception e) {
lookup = null;
}
}
lookupConstructor = lookup;
}
invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//如果当前调用的是Object类中定义的方法则不对其进行代理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {//如果调用的是Mapper接口中定义的方法则调用其代理方法
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
cachedInvoker
cachedInvoker方法主要用于维护methodCache集合,涉及的MapperMethodInvoker接口及其实现类会在后面分析,代码如下:
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
// It should be removed once the fix is backported to Java 8 or
// MyBatis drops Java 8 support. See gh-1929
//如果methodCache已经缓存了对应的MapperMethod则直接返回
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
//否则将其添加到methodCache集合中
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {//如果是default关键字定义的方法
try {
if (privateLookupInMethod == null) {
//获取method对应的方法句柄,并创建DefaultMethodInvoker对象
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {//是Mapper接口中定义的接口,通过创建的MapperMethod实例构造PlainMethodInvoker对象
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
内部类
MapperMethod调用器接口及其实现类存在的意义在于屏蔽default方法和Mapper接口中定义的方法在调用方式上的差异,对上层暴露一个统一的API。
MapperMethod调用器接口定义如下:
interface MapperMethodInvoker {
Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}
default修饰的默认方法的调用器实现
private static class DefaultMethodInvoker implements MapperMethodInvoker {
private final MethodHandle methodHandle;
public DefaultMethodInvoker(MethodHandle methodHandle) {
super();
this.methodHandle = methodHandle;
}
//通过方法句柄调用
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return methodHandle.bindTo(proxy).invokeWithArguments(args);
}
}
Mapper接口中定义的方法的调用器实现
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}
MapperMethod调用器接口及其实现类的类图如下:
getMethodHandleJava8
将method对象转换成一个方法句柄
private MethodHandle getMethodHandleJava8(Method method)
throws IllegalAccessException, InstantiationException, InvocationTargetException {
final Class<?> declaringClass = method.getDeclaringClass();
//unreflectSpecial 用于将method对象转换成一个方法句柄
return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
}
MapperMethod
MapperMethod类中保存着Mapper接口中定义的所有方法的信息,以及该方法对应的SQL语句的信息,MapperMethod是Method到sql映射的桥梁。
属性
private final SqlCommand command;//记录了sql语句的namespace和类型
private final MethodSignature method;//记录Mapper接口中定义的方法相关的信息
SqlCommand和MethodSignature都是MapperMethod类中定义的内部类,接下来分析这两个内部类的代码
内部类
ParamMap
只重写了HashMap的get方法,当key不存在时,抛出一个异常
public static class ParamMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -2212268410512043556L;
@Override
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
}
return super.get(key);
}
}
SqlCommand
public static class SqlCommand {
//sql语句的名称,namespace + sqlId
private final String name;
//sql语句的类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
private final SqlCommandType type;
//构造函数,用于初始化name和type属性的值
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {//处理@Flush注解
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);
}
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
/**
* 递归向上查找所有父类,定位定义方法的接口
* @param mapperInterface 被代理的接口
* @param methodName 方法名
* @param declaringClass 生命方法的类
* @param configuration
* @return
*/
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
//递归退出条件,表示在mapperInterface的整个继承树中没有找到
return null;
}
//递归向上查找整个继承树
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
MethodSignature
属性
//是否返回多个结果,即返回的类型是否是Collection或者数组
private final boolean returnsMany;
//返回的结果是否是Map类型
private final boolean returnsMap;
//是否为空返回
private final boolean returnsVoid;
//是否返回游标
private final boolean returnsCursor;
//是否返回一个Optional对象,jdk8中引入
private final boolean returnsOptional;
//返回的类型
private final Class<?> returnType;
//key的名称,前提是返回的类型必须是Map类型,即returnsMap应该为true
//不理解这个属性的意思的话,可以google一下@MapKey注解的功能就懂了
private final String mapKey;
//ResultHandler在方法形参中的位置
private final Integer resultHandlerIndex;
//RowBounds在方法形参中的位置
private final Integer rowBoundsIndex;
//参数名称解析器
private final ParamNameResolver paramNameResolver;
构造函数
//对上面所有属性进行初始化
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
//调用org.apache.ibatis.reflection.TypeParameterResolver解析方法的返回值类型
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);
//调用Reflector包下的ObjectFactory判断方法的返回值类型是否是集合或数组
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
//获取RowBounds在方法形参中的位置索引
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
//获取ResultHandler在方法形参中的位置索引
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
getUniqueParamIndex
用于定位paramType在方法形参中的位置索引,索引从0开始
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;
}
ParamNameResolver
参数名称解析器,用于处理Mapper接口中定义的方法的参数列表
public class ParamNameResolver {
public static final String GENERIC_NAME_PREFIX = "param";
private final boolean useActualParamName;
private final SortedMap<Integer, String> names;
private boolean hasParamAnnotation;
public ParamNameResolver(Configuration config, Method method) {
//是否允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项 默认为true
this.useActualParamName = config.isUseActualParamName();
//获取方法的参数类型列表
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
//遍历的长度为paramCount即带注解的参数个数,所以下面遍历注解集合的时候不存在索引越界异常
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
//排除RowBounds和ResultHandler这类非业务的功能性参数
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;//只要有一个参数被@Param注解修饰,该属性就为true
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
//没有被@Param注解修饰,但是允许使用方法签名中的名称作为语句参数名称,则使用方法签名中的名称
if (useActualParamName) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
//如果当前形参既没有被@Param注解修饰,useActualParamName属性也为false
//则当前参数的name为数组的size
name = String.valueOf(map.size());
}
}
//如果方法的形参中没有定义RowBounds和ResultHandler,且所有的形参都没有被@Param注解修饰
//在这种理想情况下map中的值为{0="0",1="1",2="2",3="3"}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
private String getActualParamName(Method method, int paramIndex) {
return ParamNameUtil.getParamNames(method).get(paramIndex);
}
private static boolean isSpecialParameter(Class<?> clazz) {
return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
}
public String[] getNames() {
return names.values().toArray(new String[0]);
}
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {//无参的情况
return null;
} else if (!hasParamAnnotation && paramCount == 1) {//只有一个参数的情况,可以做特殊处理
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);//name -> 实参
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {//如果用户在@Param注解中声明的变量名称就是Params+索引的格式,则跳过
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
if (object instanceof Collection) {
ParamMap<Object> map = new ParamMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
} else if (object != null && object.getClass().isArray()) {
ParamMap<Object> map = new ParamMap<>();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
}
return object;
}
}
推荐阅读
-
【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)
-
MyBatis源码解析 - 反射模块
-
Mybatis源码学习(15)-binding模块之MapperMethod类
-
mybatis源码学习------resultMap和sql片段的解析
-
mybatis源码学习------动态sql的解析(SqlSource)
-
mybatis源码解析之基础模块-log
-
Mybatis 源码阅读----事务模块(Transaction)
-
MyBatis源码解析(二) 基础支持层反射模块Reflector
-
mybatis源码解析之基础模块-TypeHandler
-
啃下MyBatis源码 - MyBatis核心流程三大阶段之代理阶段(binding模块分析)