入口:一般会在DAO层配置中,比如下面是我的配置
在这里面,我们配置了id为sqlSessionFactory的bean。那么这也是我们的入口
1 SqlSessionFactoryBean
- SqlSessionFactoryBean实现了FactoryBean,那么最终Spring返回的应该是getObject方法的返回值,
SqlSessionFactoryBean.getObject
->afterPropertiesSet()
->buildSqlSessionFactory
->this.sqlSessionFactoryBuilder.build(configuration)
->SqlSessionFactoryBuilder.build(Configuration config)
->new DefaultSqlSessionFactory(config);
由此可以看出,id为sqlSessionFactory的bean默认是DefaultSqlSessionFactory。Mybatis的官方推荐,SqlSessionFactory最好是做成单例的,不需要频繁创建。
2 获取session
通过api获取session的方式
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
那么我们就看DefaultSqlSessionFactory.openSession方法,如下
public class DefaultSqlSessionFactory implements SqlSessionFactory{
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务管理器
Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
//返回一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
}
}
再看DefaultSqlSession.getMapper方法
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
这个configuration
对象是在哪里创建,又是哪里传入的呢?其实是从一开始的SqlSessionFactoryBean
中创建好后,赋值给SqlSessionFactoryBuilder
的build的方法参数,一路传到了DefaultSqlSession
中。也就是说configuration
是隶属于DefaultSqlSessionFactory
的,生命周期很长。
那configuration
究竟有什么呢?
public class Configuration {
//Mapper 接口一开始被添加到在SqlSessionFactory中的名为 MapperRegistry 类的
//HashMap中, key = Mapper class value = 创建当前Mapper的工厂。
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
...
原来,所有的Mapper接口被扫描后,都会被存储在Configuration的mapperRegistry里的Map中。而且key = Mapper class
value = 创建当前Mapper的工厂
。
接着看configuration.getMapper(..)
方法
//存储Mapper接口和创建Mapper的工厂
private final Map, MapperProxyFactory>> knownMappers = new HashMap<>();
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
return mapperProxyFactory.newInstance(sqlSession);
}
那我们知道了knownMappers.get(type)
返回的是一个创建Mapper的工厂MapperProxyFactory
。
接下来我们看看 MapperProxyFactory
是如何创建的。直接看MapperProxyFactory
的newInstance(SqlSession sqlSession)
方法
public class MapperProxyFactory {
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
可以清楚的看到,通过jdk的动态代理创建了代理类。那么我们需要关注一下,代理类的InvocationHandler做了哪些事情
public class MapperProxy implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//先获取缓存起来的MapperMethod 对象。这个Method对象对应的就是我们在Mapper接口里写的方法
final MapperMethod mapperMethod = cachedMapperMethod(method);
//这里才是执行数据库操作的地方
return mapperMethod.execute(sqlSession, args);
}
}
private MapperMethod cachedMapperMethod(Method method) {
//这里需要注意methodCache这个Map,不是保存在本类里的。
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
//构造
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
这里需要注意methodCache这个Map,不是保存在本类里的。看MapperProxy的有参构造传进来的。那么谁创建的MapperProxy呢?上面我们说了是Mapper对应的Mapper工厂MapperProxyFactory。这个对象也是保存在Configuration
中,生命周期很长。
这就解释了,为什么 官网上推荐,让Mapper的实现类 用后就可以舍弃(做局部变量),但是这样每次都去创建难道不耗时,答案是不耗时,因为核心的MapperMethod
都已经被缓存起来了。