现在开发中,我们很少单独使用Mybatis,都是整合Spring框架一起使用,这便涉及到Mapper装载到Spring容器的问题。那首先Mapper是个接口,我们为什么不需要写实现类,Mapper又是如何找到实现类的呢?其次Mapper的实现类又是如何装载到Spring容器里的呢?
- 关于Mapper如何找到实现类,相信很多同学都知道是通过动态代理实现,下面我们具体看一下整个过程。 首先说一下Mybatis的整体工作流程,大致分为以下几步:
- 构建SqlSessionFactory,现在我们使用springboot框架都是通过代码形式构建
- 通过SqlSessionFactory来获取SqlSession
- 通过SqlSession来获取Mapper
- 执行Mapper具体的方法
重点就是通过SqlSession获取Mapper的过程,DefaultSqlSession中调用Configuration对象的getMapper方法
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
Configuration中调用MapperRegistry的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry中有个knownMappers变量,是个Map对象,key便是Mapper的class对象,value是MapperProxyFactory,在启动初始化阶段所有Mapper会被注册到MapperRegistry当中。MapperRegistry中getMapper的具体实现如下:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
可以看到通过获取的MapperProxyFactory,最终生成MapperProxy代理对象
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
···省略部分代码
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
执行MapperProxy的invoke方法,若是Mapper中Method非默认方法,最终会通过PlainMethodInvoker生成一个MapperMethod对象,最终执行mapperMethod的excute方法。
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return this.mapperMethod.execute(sqlSession, args);
}
MapperMethod的excute方法,会通过SqlSession去执行sql。
- 为什么在Spring的容器中可以注入Mybatis的Mapper接口的动态代理对象呢?
通过@Mapper或@MapperScan注解,或者使用MapperScannerConfigurer这个类,可以把每个接口对应的MapperFactoryBean注册到Spring容器中。如下代码:
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
}
我们知道将FactoryBean注入到容器,当我们getBean的时候,其实最终是通过FactoryBean的getObject()方法来获取bean,可以看到MapperFactoryBean的getObject()方法实现就是通过SqlSession来获取Mapper接口的代理对象,这样就又回到了我们上面所说的怎样获取Mapper的实现类。