mybatis的工厂与代理实现

2022-06-20 20:13:44 浏览数 (1)

在文前中已经提到过, 除了使用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.

0 人点赞