前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mybatis的工厂与代理实现

mybatis的工厂与代理实现

作者头像
一个架构师
发布2022-06-20 20:13:44
2210
发布2022-06-20 20:13:44
举报

在文前中已经提到过, 除了使用XML配置的方式, 还可以使用包扫描和接口的方式配置mapper. 其中包扫描的方式底层也是封装了接口配置方式实现的. 今天就一起看接口配置方式是如何实现的?

一. mapper接口添加

mapper接口也是添加configuration配置类中, 由全局配置统一管理. 对configuration不了解的, 可以参考一分钟进入mybatis的世界.

代码语言:javascript
复制
configuration.addMapper(CountryMapper.class);

1. mapper注册, configuration添加mapper时, 会通过MapperRegistry进行记录和解析.

mapper接口会被封装成MapperProxyFactory并保存knownMappers集合中; MapperAnnotationBuilder会解析mapper并生成MappedStatement; 这里留一个问题,mapper为什么是封装成工厂(MapperProxyFactory)而不是具体代理(MapperProxy)呢?

代码语言:javascript
复制
public <T> void addMapper(Class<T> type) {
// ...
  knownMappers.put(type, new MapperProxyFactory<>(type));
  MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  parser.parse();
// ...
}

二. mapper执行

与XML方式类似也是从SqlSession中获取mapper代理对象

代码语言:javascript
复制
SqlSession sqlSession = sqlSessionFactory.openSession();
CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);

1. 从knownMappers集合中获得MapperProxyFactory对象, 并根据当前SqlSession创建mapperProxy对象.

代码语言:javascript
复制
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  // ...
    return mapperProxyFactory.newInstance(sqlSession);
}

2. 创建mapper代理对象MapperProxy 到这里也就可以回答上面的问题了, 为什么初始化时mapper封装的是MapperProxyFactory, 而不是mapperProxy? 原因就在于, 每个mapper在执行时是要绑定具体SqlSession, 处理事务,SQL连接等操作的, 所以每次需要根据上下文创建新的mapperProxy.

代码语言:javascript
复制
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

3. 代理对象MapperProxy, 实现InvocationHandler代理接口

代码语言:javascript
复制
public class MapperProxy<T> implements InvocationHandler, Serializable {
// ...
}

4. 查看代理对象MapperProxy的invoke()方法, SQL执行逻辑在MapperMethod类中.

代码语言:javascript
复制
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ...
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

5. 跟进MapperMethod对象的执行方法, 可以发现最后还是执行了sqlSession.selectList()方法, 获取MappedStatement对象执行的. 和前篇文章分析的mybatis执行方式是一样的.

代码语言:javascript
复制
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
// ...    
RowBounds rowBounds = method.extractRowBounds(args);    
result = sqlSession.selectList(command.getName(), param, rowBounds);
// ...  
return result;
}

总结

通过注解方式定义mapper接口, 通过代理工厂(MapperProxyFactory)实现动态代理, 每次使用时,会根据SqlSession创建代理对象(mapperProxy), 最终还是会通过MappedStatement对象执行SQL.

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 从码农的全世界路过 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档