最近在翻看Mybatis 、 Mp源码。 看到了 @Mapper 注解扫描, MapperProxy , MapperProxyFactory这些类。
梳理一下,@Mapper到底是怎么工作的。
1. 意图
Mybatis 期望提供一种”便捷”、”所见即所得” 的方法,通过调用java方法的方式,来执行XML中的sql。
它最原始工作时的样子:
1 2 3 4 5 6 7 8 9 10
| @Mapper public interface MyMapper { public String foo(String name); }
public void main(){ SqlSession sql = ....; MyMapper proxy = SqlSession.getMapper(MyMapper.class); String str = proxy.foo("semghh"); }
|
Mybatis返回给我们一个 MyMapper的java代理对象。我们只需要调用 MyMapper.foo() 就可以找到对应的sql语句并调用。
2. 他是怎么做到的?
基于JDK动态代理。
相信很多人见过使用JDK动态代理做 AOP,但实际上动态代理还能作更多的东西。
我们一定都见过这样的aop代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public interface MyService { public String hello(String name); }
public class MyServiceImpl implements MyService { public String hello(String name){ return "hello,name"; } }
public class MyServiceProxy implements InvocationHandler { private MyService origin; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object o = method.invoke(origin,args); return o; } }
public void foo(){ MyServiceImpl impl = ...; MyService o = (MyService) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{MyService.class}, new MyServiceProxy(impl)); o.hello("123"); }
|
mybatis 是怎么把 Mapper.java 映射到 —> XML sql 的呢?
实际上很简单 : 传入impl , 并在invoke实现一个映射转换即可。
1 2 3 4 5 6 7 8
| public class MapperProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object o = .... return o; } }
|
3.深入源码
3.1 获得Mapper.java 流程
从SqlSession#getMapper()中一路跟进 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
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); } }
|
Mapper代理工厂是哪里创建的?
mybatis扫描@Mapper ,并为每一个Mapper.java 创建 MapperProxyFactory 对象。 存放到knownMappers(ConcurrentHashMap)中。
3.2 创建proxy过程详解
所有的MapperProxyFactory对象都会存储到 MapperRegistry对象中。
MapperProxyFactory (Mapper代理工厂) 存储了代理的原始 Mapper.java 引用 (mapperInterface)
随后给代理方法做了一层缓存,存储到 ConcurrentHashMap中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class MapperProxyFactory<T> {
private final Class<T> mapperInterface; 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); }
public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
}
|
MapperMethodInvoker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
interface MapperMethodInvoker { Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable; }
private static class PlainMethodInvoker implements MapperMethodInvoker { private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) { this.mapperMethod = mapperMethod; }
@Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return mapperMethod.execute(sqlSession, args); } }
private static class DefaultMethodInvoker implements MapperMethodInvoker { private final MethodHandle methodHandle;
public DefaultMethodInvoker(MethodHandle methodHandle) { this.methodHandle = methodHandle; }
@Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return methodHandle.bindTo(proxy).invokeWithArguments(args); } }
|
可以看到 使用了方法句柄 ,它要比一般的反射调用方法快。 还做了jdk8/9的兼容。
对于非deafult方法,将使用PlainMethodInvoker调用。
对于default方法,将使用DefaultMethodInvoker调用。
MapperProxy
实现了 InvocationHandler , 是整个代理过程的核心 , 为Mapper.java中的每一个方法做了缓存。
解析、生成对应的 MethodInvoker。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable { try { return MapUtil.computeIfAbsent(methodCache, method, m -> { if (!m.isDefault()) { return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } try { if (privateLookupInMethod == null) { return new DefaultMethodInvoker(getMethodHandleJava8(method)); } return new DefaultMethodInvoker(getMethodHandleJava9(method)); } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } }); } catch (RuntimeException re) { Throwable cause = re.getCause(); throw cause == null ? re : cause; } }
|
创建SqlCommand & 方法签名
在创建 MethodInvocker的时候,new了一个 MapperMethod ,在MapperMethod的构造器中,解析并根据 statementId匹配了MappedStatement 。
1 2 3 4 5 6
| public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
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) { throw new BindingException( "Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName); } name = null; type = SqlCommandType.FLUSH; } else { name = ms.getId(); type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } }
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) { String statementId = mapperInterface.getName() + "." + methodName; if (configuration.hasStatement(statementId)) { return configuration.getMappedStatement(statementId); } if (mapperInterface.equals(declaringClass)) { 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; }
|
同样的,MapperMethod 构造器中也创建了方法签名,它是做什么的?
方法签名记录了:
- 返回值特点: 是否集合。 是否Map , 是否void 是否Cursor ,是否Optional (对于这些特点可以进行返回值特殊处理)
- 返回值类型:
Class<?>
- 参数命名解析器