聊聊Mybatis的binding模块

聊聊MyBatis的binding模块为什么我们在使用Mybatis的时候只需要写接口和xml文件就能执行sql呢?这就是Mybatis的binding模块需要做的事情了,今天我们分析一下Mybatis的binding模块,binding包下的类主要有四个MApperRegistry、MapperProxyFactory、MapperProxy和MapperMethod
映射注册类MapperRegistryMapperRegistry是个注册类,它的knownMappers集合保存着Mapper接口和MapperProxyFactory实例的映射关系
它的addMapper()方法就是添加映射关系的方法:
public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {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);}}}}当传入的type是个接口并knownMappers中没有,就把type和对应的MapperProxyFactory实例放入knownMappers中
然后在执行sql的时候,Mybatis会调用MapperRegistry.getMapper()方法
@SuppressWarnings("unchecked")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 {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}

  1. 先从knownMappers集合中找到对应的MapperProxyFactory实例
  2. 然后调用newInstance()方法
  3. 映射代理工厂类MapperProxyFactory
  4. MapperProxyFactory的newInstance()方法:
public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}生成mapperInterface接口 的代理对象的实例,代理类是MapperProxy,它实现了InvocationHandler,利用jdk动态代理生成代理对象
看一下它重写的invoke()方法:
映射代理类MapperProxyMapperProxy的invoke()方法:
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}不是Object的类会被拦截,拦截调用cachedInvoker()方法,其他执行invoke()方法,
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {return MapUtil.computeIfAbsent(methodCache, 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;}}
  1. 从methodCache中获取对应的MapperMethodInvoker
  2. 如果缓存没有,如果是方法是default方法就创建DefaultMethodInvoker对象,否则创建PlainMethodInvoker对象
  3. 默认方法调用类DefaultMethodInvoker
  4. 对于DefaultMethodInvoker类通过MethodHandle完成调用
private static class DefaultMethodInvoker implements MapperMethodInvoker {private final MethodHandle methodHandle;public DefaultMethodInvoker(MethodHandle methodHandle) {super();this.methodHandle = methodHandle;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return methodHandle.bindTo(proxy).invokeWithArguments(args);}}对于普通方法对应的类PlainMethodInvoker通过MapperMethod来完成调用,
private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {super();this.mapperMethod = mapperMethod;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);}}


推荐阅读