你是否也曾好奇MyBatis中的Mapper当中的方法是如何被执行?这篇文章通过源码解析其中的流程。
概述
MyBatis通过JDK动态代理的方式创建Mapper接口的代理对象,当Mapper代理对象调用Mapper接口中定义的方法时,会调用InvocationHandler
中的invoke()
方法,执行相应的拦截逻辑,从而完成数据库操作。
执行过程
我们知道Mapper由两部分组成:Mapper接口和通过注解或者XML文件配置的SQL语句。以下通过一个单元测试的代码来看如何执行Mapper中定义的方法:
//加载主配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml")
//通过主配置文件信息构建 sqlSessionFactor,在这里会加载Mapper的配置信息:调用MapperRegistry类中的addMapper方法将信息放入缓存中
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//获取SqlSession实例
SqlSession session = sqlMapper.openSession()
//通过动态代理获取Mapper代理对象
AuthorMapper mapper = session.getMapper(AuthorMapper.class);
//执行Mapper中的方法:会被代理类中的invoke方法拦截
List<Author> authors = mapper.selectAllAuthors();
跟踪源码,可以看到完整的流程:
1、DefaultSqlSession.java: SqlSession接口的默认实现类
//SqlSession中定义了获取Mapper对象的方法,DefaultSqlSession中实现了这个方法:实际上是调用Configuration类中getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
2、Configuration.java: 用于封装配置文件信息的类
//Mapper注册类实例
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
3、MapperRegistry.java:Mapper信息注册和获取
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
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);
}
}
4、MapperProxyFactory.java:Mapper代理工厂类
//通过 Proxy.newProxyInstance 生成代理对象
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
//通过Proxy类获取代理对象实例
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
总结一下:MyBatis框架在应用启动时会解析所有的Mapper接口,然后调用MapperRegistry对象的addMapper()方法将Mapper接口信息和对应的MapperProxyFactory(Mapper代理工厂类)对象注册到knownMappers缓存中。所以可以直接从缓存中获取Mapper接口类型对应MapperProxyFactory对象,从而构造出MapperProxy实例。
通用的执行逻辑
在MapperProxy类中定义了通用的执行逻辑,当调用Mapper当中的方法时便会执行这些逻辑:MapperProxy中的invoke方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//从Object类中继承的方法不做处理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
//注:cachedInvoker(method).invoke会执行 mapperMethod.execute(sqlSession, args)方法,完成对数据库的操作
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
个人博客:https://www.kangpeiqin.cn/#/index
欢迎与我交流,关注公众号,获取更多技术相关知识: