MyBatis 框架的动态代理是通过两个步骤完成的:
- 加载配置文件时,通过扫描到的接口类创建代理工厂类,并将其依次放到一个 Map 中:
大致的创建动态代理类的流程图如下:
MapperRegistry.class
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 的 Map 中
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
- 当我们调用接口方法时,MyBatis 会根据我们传入的接口类型来生成一个动态代理类
MapperRegistry.class
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);
}
}
MapperProxyFactory.class
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);
}
创建完代理实例后,就可以正常调用接口中的查询方法。
当调用接口中的方法时,代理类会将代理实例上所有方法的调用统统转到 MapperMethod 类的 execute( ) 方法上。
MapperMethod.class
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
// do insert ...
}
case UPDATE: {
// do update ...
}
case DELETE: {
// do delete ...
}
case SELECT:
// do select ...
break;
case FLUSH:
// do flush ...
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
// do other things ...
}
最后,一张图总结一下 MyBatis 动态代理的大致过程: