mybatis

最近在翻看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 = ....; //创建SqlSession
MyMapper proxy = SqlSession.getMapper(MyMapper.class);//Mybatis返回给我们一个 MyMapper的java代理对象。
String str = proxy.foo("semghh");//我们只需要调用 `MyMapper.foo()` 就可以找到对应的sql并调用。
}

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 {
//start
Object o = method.invoke(origin,args);
//end
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
//DefaultSqlSession.java
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}

//Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}


//MapperRegistry
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);
}
}

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;
}

//JDK动态代理
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

//先创建了一个 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
//MapperProxy.java 内部接口

interface MapperMethodInvoker {
Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
//比 InvocationHandler.invoke多了一个SqlSession参数
}

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 {
//jdk的方法句柄
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()) {
//非default方法直接创建 PlainMethodInvoker并存放到map中
//可以看到,这里创建了一个类: MapperMethod, 它在创建的过程中完成了解析操作。
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) {
//创建 SqlCommand 。 sqlcommand的从MappedStatement中取出了sql执行类型(select ,update ,insert,delete)
//根据sql执行类型做不同的操作。
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
//MapperMethod.java


public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
//解析映射语句,并从Configuration中取出对应的 MappedStatement (映射语句)。
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(); //设置一下 name = MappedStatemet.id
type = ms.getSqlCommandType(); //设置一下sql命令的类型
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}



//从Configuration中,取出 Mapper.java对应的 MappedStatement 映射语句。
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<?>
  • 参数命名解析器

mybatis
http://example.com/2025/07/09/2025-07-08-mybatis-mapper-proxy/
作者
John Doe
发布于
2025年7月9日
许可协议