引子
MyBatis是优秀的半自动ORM框架。
其工作的步骤可以大致分为以下步骤:
- 读取配置文件并返回InputStream流对象
- 依据InputStream对象,解析出Configuration对象,然后创建SqlSessionFactory工厂对象
- 从SqlSessionFactory对象中创建SqlSession
- 从SqlSession中调用Excutor执行数据库操作,生成具体的SQL语句
- 对执行结果进行处理、封装
- 提交事务
以上步骤是初学MyBatis框架时就耳熟能详的,也不是本文的探讨的重点。
本文探讨的是:Dao接口是如何和Mapper文件对应上的呢?
解析
Dao接口没有实现类,那我们执行的时候,它最终是怎样执行SQL语句的呢?
如果了解过Spring代理,就基本上能猜出MyBatis是基于动态代理的。
秘密就在sqlSession.getMapper()中:
//DefaultSqlSession中的getMapper
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
//configuration中的给getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
//MapperRegistry中的getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//从MapperRegistry中的HashMap中拿MapperProxyFactory
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类中的newInstance方法
public T newInstance(SqlSession sqlSession) {
// 创建了JDK动态代理的Handler类
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 调用了重载方法
return newInstance(mapperProxy);
}
//MapperProxy类,实现了InvocationHandler接口
public class MapperProxy<T> implements InvocationHandler, Serializable {
//省略部分源码
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
// 构造,传入了SqlSession,说明每个session中的代理对象的不同的!
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//省略部分源码
}
//重载的方法,由动态代理创建新示例返回。
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
简单来说,它就是通过JDK动态代理,返回了一个Dao接口的代理对象,这个代理对象的处理器是MapperProxy对象。所有,我们通过@Autowired注入Dao接口的时候,注入的就是这个代理对象,我们调用到Dao接口的方法时,则会调用到MapperProxy对象的invoke方法。
总结
动态代理返回示例后,我们就可以调用DAO接口中的方法了。
当我们调用Dao接口的方法时候,实际上是调用了动态代理返回的对象invoke方法。
MyBatis通过全限定名+方法名拿到MapperStatement对象,然后通过执行器Executor去执行具体SQL并返回。