mybatis底层组件介绍-binding(一)

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接口,主要提供的功能如下:

  1. 根据当前执行的Mapper接口的方法Method,找到对应的MapperMethodInvoker并且执行
  2. 对找到的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方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值