可见,sqlSession对象的初始化是new 出了SqlSessionTemplate对象,我们看SqlSessionTemplate源码:
首先,看其属性:private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator;可以看出,SqlSessionTemplate类本身是SqlSession对象,而其成员属性也有一个SqlSession对象,说明这是装饰者模式,干活的肯定是sqlSessionProxy对象,下面看sqlSessionProxy的初始化:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); }可以看出,sqlSessionProxy 是通过jdk动态代理生成的代理对象,重点分析InvocationHandler的逻辑,这里是对方法的加强,所以看SqlSessionInterceptor源码:
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }先查看getSqlSession方法源码,该方法调用的是SqlSessionUtils类里的方法:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }分析其源码,先分析这段:
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);看getResource方法源码:
public static Object getResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Object value = doGetResource(actualKey); if (value != null && logger.isTraceEnabled()) { logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } return value; }再看doGetResource方法源码:
private static Object doGetResource(Object actualKey) { Map可见,最终返回的是resources属性中的一个值,我们看resources的类型:
private static final ThreadLocal可见,这是一个ThreadLocal类,也就是说,SqlSessionUtils类中的getSqlSession方法,先从ThreadLocal中获取当前线程的SqlSession对象,如果有,则直接返回,如果没有,则运行如下代码,创建sqlSession对象:
session = sessionFactory.openSession(executorType);因此,对于一个mapper接口而言,一个线程中只存在一个sqlsession对象。
我们继续分析代理对象sqlSessionProxy的代理参数InvokeHandler之SqlSessionInterceptor 源码:finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } }可见,最终,要对sqlsession进行关闭 *** 作。所以,对于sqlsession而言,就是一个线程的生命周期,在一个线程中创建,并在一个线程中关闭。
- getMapper方法研究:
上面我们分析了MapperFactoryBean类中getObject方法中getSqlSession方法的来龙去脉,可知一个线程获取到了一个SqlSession对象,下面我们继续研究getObject方法中getMapperr方法的源码:
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }从代码来看getMapper是讲Mapper接口作为参数传入,然后返回其代理对象到Spring容器中,看其源码:
因为MyBatis和Spring融合了,所以看Spring提供的SqlSessionTemplate中的getMapper方法,一顿跳入,最终点进了这个方法:publicT getMapper(Class type, SqlSession sqlSession) { final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory ) 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); } } 可以看到,getMapper方法首先获得了MapperProxyFactory对象,然后调用MapperProxyFactory的newInstance方法,获得Mapper的代理对象,看MapperProxyFactory源码:
public class MapperProxyFactory{ private final Class mapperInterface; private final Map methodCache = new ConcurrentHashMap (); public MapperProxyFactory(Class mapperInterface) { this.mapperInterface = mapperInterface; } public Class getMapperInterface() { return mapperInterface; } public Map getMethodCache() { return methodCache; } @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); } } 可以看出,newInstance方法也是用了jdk的动态代理,生成的代理类,看主要参数InvocationHandler的实现类MapperProxy的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }最终,调用mapper的方法,进行执行。至于代理类是如何从xml中获取到sql,然后形成语句,发送数据库执行的,这里不做研究。
总结从上面可以看到,MyBatis的设计思路大致如下:
1.程序员手动或自动(SpringBoot项目)配置SqlSessionFactoryBean类到Spring容器,SqlSessionFactoryBean是一个FactoryBean,通过getObject方法将SqlSessionFactory加入到Spring容器中,同时也将MyBatis的大杂烩Configuration对象加入到Spring容器。所以,SqlSessionFactory是伴随项目运行始终的,所以,二级缓存就放在了SqlSessionFactory中,只要Spring容器不销毁,SqlSessionFactory就一直存在。在分布式场景下,多个项目,就是多个Spring容器,也就是多个SqlSessionFactory,所以,并不能公用二级缓存,在分布式业务场景下,MyBatis的二级分布式缓存就显得很鸡肋。2.程序员配置MapperScannerConfigurer类和扫描包,MapperScannerConfigurer是一个BeanDefinitionRegistryPostProcessor,可以注册BeanDefinition对象,其扫描到一个mapper后,就会注册一个BeanDefinition对象,注册的BeanDefinition是MapperFactoryBean类对象,mapper接口以mapperinterfaces属性存入MapperFactoryBean中。
3.MapperFactoryBean类也是个FactoryBean对象,其getObject方法将mapper的代理类放入到Spring容器中。
4.MapperFactoryBean类是通过SqlSession对象的代理对象获取到的mapper的代理对象,在一个线程中,一个mapper只会有一个sqlsession对象,也只生成一个mapper的代理对象,当线程方法运行完后,会把sqlsession对象关闭,所以,每个线程都是新的sqlsession对象和新的mapper代理对象。
5.sqlsession对象也用代理对象的目的是为了加强sqlsession对象,对其进行关闭等 *** 作,所以用了代理。二级缓存在sqlsession中,所以,每个线程有自己的二级缓存,不能公用,而在一个线程中,同一个方法查询两次的 *** 作也不多,所以说,MyBatis的二级缓存也很鸡肋。
欢迎分享,转载请注明来源:内存溢出
从配置文件研究MyBatis的运行过程
一、Spring整合MyBatis配置文件
赞
(0)
打赏
微信扫一扫
支付宝扫一扫
spring框架学习 - spring IoC 之 基于注解的容器配置补充
上一篇
2022-12-17
狂神 - 文件上传(重要)
下一篇
2022-12-17
评论列表(0条)