mybatis已经成了系统开发必备的orm框架,开发者只需要提供mapper接口,就可以执行sql,返回预期结果。接口是不能直接运行的,那么mybatis是如何通过接口调用执行sql的?

道理似乎很简单,只有接口没有实现,那肯定是通过动态代理生成的实现类。那mybatis又是如何通过动态代理来执行sql的呢?很多童鞋到这一步可能就哑火了,本文将告诉你答案。

先来看下mybatis的基本流程,引用自官网。通过读取全局配置文件构建sqlSessionFactory,然后拿到sqlSession,有了sqlSession就可以执行sql。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession()

配置文件mybatis-config.xml里承载了许多重要的信息,诸如typeAliases,environments等(暂不讨论),本文主要分析mapper动态代理,mapper接口不仅需要实现,还需要和mapper.xml文件关联起来。

跟踪一下解析mybatis-config.xml的过程,会发现配置文件最终会被解析并生成Configuration对象,所有的配置信息都在这里了。mappers节点内容会被添加到MapperRegistry,

protected final MapperRegistry mapperRegistry;
public <T> void addMapper(Class<T> type) {
    this.mapperRegistry.addMapper(type);
}

再来看下MapperRegistry的相关方法,在addMapper中会将目标接口类型构建MapperProxyFactory,看名称就知道是一个mapper代理类工厂,离代理对象已经一步之遥了。

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public <T> void addMapper(Class<T> type) {
    this.knownMappers.put(type, new MapperProxyFactory(type));
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    return mapperProxyFactory.newInstance(sqlSession);
}

通过getMapper方法可以看到会返回代理类的实现MapperProxyFactory,我们知道jdk动态代理是通过Proxy和InvocationHandler实现的,可以猜测代理实现就是在MapperProxyFactory里通过Proxy完成的。

public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

看到了熟悉的Proxy.newProxyInstance(jdk动态代理),还差一个InvocationHandler,可以看到对应的位置是MapperProxy。

public class MapperProxy<T> implements InvocationHandler, Serializable{ 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }
}

这样动态代理实现类就完成了,mapper接口方法调用时,会触发代理类的invoke方法。剩余的工作就是根据类型.方法名找到对应的MappedStatement,最后查询数据库。

本文简要的分析了mybatis代理实现过程,还有许多细节地方没有带出,感兴趣的童鞋可以自行查看mybatis源码,跟踪一下,大有裨益。

觉得有用,点个关注。欢迎关注同名公众号【码农小麦】,感谢老铁。

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐