聊聊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);}}
- 先从knownMappers集合中找到对应的MapperProxyFactory实例
- 然后调用newInstance()方法
- 映射代理工厂类MapperProxyFactory
- 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;}}
- 从methodCache中获取对应的MapperMethodInvoker
- 如果缓存没有,如果是方法是default方法就创建DefaultMethodInvoker对象,否则创建PlainMethodInvoker对象
- 默认方法调用类DefaultMethodInvoker
- 对于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);}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 护士/护理人员的完整简历范文 护士简历范文
- |职场中有哪些常见的套路?
- |职场上,面子不是所谓的客气,而是让自己有了自信,让自己成功
- 养狗好处vs养狗坏处 养宠物的利弊
- 配音|余生,做一个能扛事的成年人
- 秋作文400字18篇作文 秋天的作文400字
- 翡翠手镯|翡翠手镯的价值,傻瓜教程,教你这样看懂翡翠手镯的色泽
- 短发|爱美的美女看过来,你知道秋天为什么要补水吗?
- 无基础唱歌技巧和发声方式教学 学习唱歌的方法
- 全球变暖的危害有哪些 全球变暖的后果