binding
我们在使用mybatis的时候,通常都是定义一个叫XXMapper的接口,这个接口具有若干和数据库相关的方法,然后会创建相应的xml文件,在xml文件中会有很多sql语句,并且会为这些sql语句代码块进行命名,这些命名会和XXMapper接口中的每个方法对应,然后在service中注入这个XXMapper就可以操作数据库了
mybatis会为我们动态地生成这个接口的实现类,并且将实现类的每个方法和xml中的sql代码块关联起来,这部分关联操作就是通过Mymbatis中的binding模块完成的,binding模块主要包括:MapperRegistry,MapperProxyFactory,MapperMethod,MapperProxy
这先介绍MapperRegistry,MapperProxyFactory,MapperProxy
MapperRegistry
MapperRegistry主要的作用就是注册Mapper和提供Mapper接口的代理类
重要属性
// mybatis的全局配置对象
private final Configuration config;
// 用来存放当前已经解析过的Mapper接口类和对应的Mappe代理类工厂的映射
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
重要方法
addMapper
public <T> void addMapper(Class<T> type) {
// 只对接口进行处理
if (type.isInterface()) {
// 判断knownMappers中是否存在当前接口
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 将当前接口加入到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.
// 解析Mapper接口中方法上的注解,比如@Insert @Select等,这里先不展开,后续会详细介绍通过xml文件和基于注解的配置解析
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
getMapper
使用getMapper来获取指定Mapper接口的实现类,从knownMappers中查找当前接口的代理类工厂,然后通过工厂来返回代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 这里会将sqlSession传入
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory
MapperProxyFactory是产生指定Mapper接口代理类的实例工厂
这里主要看newInstance方法
重要属性
// 当前需要代理的接口
private final Class<T> mapperInterface;
// 一个方法对象映射缓存,key是Mapper接口的方法对象,value是MapperMethodInvoker对象
// Method是干嘛的很好理解,MapperMethodInvoker在后面看MapperProxy的时候再看
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
重要方法
newInstance
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
主要的代理逻辑都集中在了MapperProxy这个类里面,接着看下MapperProxy这个类
MapperProxy
MapperProxy实现了InvocationHandler接口,主要提供的功能如下:
- 根据当前执行的Mapper接口的方法Method,找到对应的MapperMethodInvoker并且执行
- 对找到的MapperMethodInvoker实例进行了缓存,下次调用相同的Method,直接从缓存中取出对应的MapperMethod即可
重要属性
// 使用的sqlSession
private final SqlSession sqlSession;
// 当前需要代理的Mapper接口
private final Class<T> mapperInterface;
// 来自MapperProxyFactory
private final Map<Method, MapperMethodInvoker> methodCache;
重要方法
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 {
// 尝试从缓存中找到对应的MethodMapperInvoker
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
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
// 判断当前缓存中是否有当前Mapper接口中指定方法的缓存
MapperMethodInvoker invoker = methodCache.get(method);
// 有缓存,直接返回缓存
if (invoker != null) {
return invoker;
}
// 没有缓存开始添加缓存
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 这里主要看这个分支
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
接下来看下PlainMethodInvoker的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
可以看到这做的主要就是将参数透传给MapperMethod的execute方法