mybaits-spring 源码阅读随笔
Posted jerryqtqcjl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybaits-spring 源码阅读随笔相关的知识,希望对你有一定的参考价值。
mybaits-spring 源码阅读随笔(一)
1、@MapperScan
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { ... }
@MapperScan注解中通过@Import注解导入了MapperScannerRegistrar.class,源码如下:
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { ... @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0)); } } /** * 将MapperScannerConfigurer的bean信息注册到spring容器中 */ void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); ... registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); } }
熟悉spring源码的应该知道,MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,是spring的一个扩展点,我们可以通过实现这个接口动态的注册bean。(详细请看spring源码),这里mybaits注册了MapperScannerConfigurer这个类的bean信息。
private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { ... loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
2、MapperScannerRegistrar 和 ClassPathMapperScanner
接下来,我们看一下MapperScannerRegistrar这个类,源码如下:
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { ... @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } //创建mapper扫描器 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); ... scanner.registerFilters(); scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); } ... }
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,这是spring的另外一个扩展点,了解bean生命周期的童鞋应该就知道BeanDefinitionRegistryPostProcessor,是在bean实例化前最先执行的扩展点,这里创建了一个ClassPathMapperScanner,我们看一下里面具体做了些什么。
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { ... private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class; @Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { //这里扫描出来的是MapperScan配置路径下所有的接口 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { ... } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } //给mapper的bean信息设置默认属性 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); ... definition.setBeanClass(this.mapperFactoryBeanClass); ... if (!explicitFactoryUsed) { //这里非常重要,这里自动注入设置为byType,只要bean中有该属性的set方法,spring就会自动注入(调用set方法,及即使没有属性,set方法也会执行) definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } } }
ClassPathMapperScanner实现了ClassPathBeanDefinitionScanner(spring注解扫描器),主要是将MapperScan配置路径下所有的接口(mapper)扫描出来并将mapper的bean信息注册到beanFactory,这里注意,beanDefinition中设置的类型是MapperFactoryBean(实现了FactoryBean,这里就不多说FactoryBean了,详细可以自行谷歌或百度)。那么接下来我们就来研究一下MapperFactoryBean。
3、MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; ... @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } }
MapperFactoryBean继承了SqlSessionDaoSupport,我们主要看一下SqlSessionDaoSupport。
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSessionTemplate sqlSessionTemplate; //这里就是之前说到的自动注入,这里并没有SqlSessionFactory属性,但是就是会调用这个方法 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) { this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory); } } @SuppressWarnings("WeakerAccess") protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }
我们可以看到创建了一个SqlSessionTempalte,SqlSessionTempalte实现了SqlSession接口,我们看下源码:
public class SqlSessionTemplate implements SqlSession, DisposableBean { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; /** * 我们可以看到SqlSessionTemplate中创建了一个SqlSession的代理对象 */ public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { ... this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); } @Override public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); } //代理方法主要是在执行数据库操作之后把session关闭,这就是为什么mybaits的一级缓存在spring中会失效,因为每次操作之后都会把session关闭 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); ... return result; } catch (Throwable t) { ... } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } } } public final class SqlSessionUtils { public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) { ... SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); //第一次获取的时候肯定是null SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } //实际上就是执行了sessionFactory.openSession获取一个新的session session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } }
MapperFactoryBean的实例化过程中我们重点关注SqlSessionTemplate,SqlSessionTemplate中又对SqlSession做了一层代理,其目的是在每次执行完数据库操作后关闭session,当然MapperFactoryBean中还有在afterPropertiesSet中解析Mapper中注解类型的sql语句,这个会专门挑出来,和xml解析一起写一篇笔记。接下来我们看一下我们在getMapper时,底层源码干了什么事情。
我们都知道在获取容器中的factoryBean时(如果获取factoryBean本身,可以通过(&beanName)来获取),实际上是调用factoryBean.getObject,那么我们来看看MapperFactoryBean.getObject做了什么。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { @Override public T getObject() throws Exception { //getSqlSession 就是返回sqlSessionTemplate return getSqlSession().getMapper(this.mapperInterface); } } public class SqlSessionTemplate implements SqlSession, DisposableBean { @Override public <T> T getMapper(Class<T> type) { //getConfiguration()返回的就是我们在sqlSessionFactoryBean中配置的Configuration对象 return getConfiguration().getMapper(type, this); } } public class Configuration { protected final MapperRegistry mapperRegistry = new MapperRegistry(this); public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } } public class MapperRegistry { @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //mapperProxyFactory在下篇笔记中会分析其具体生成过程 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); ... try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } } /** * 返回一个mapper代理对象 */ public class MapperProxyFactory<T> { @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
我们可以看出,其实我们在获取mapper实例时,返回的是mapper的代理对象,接下来我们看一下,在执行mapper的查询方法时,mapperProxy代理方法具体做了什么操作。
public class MapperProxy<T> implements InvocationHandler, Serializable { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //符合以下情况直接执行 if (Object.class.equals(method.getDeclaringClass())) { //执行继承或重写Object的方法,如equals等 return method.invoke(this, args); } else if (method.isDefault()) { //这里执行的是接口中的默认方法 ... } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); //这里的sqlSession是sqlSessionTemplate return mapperMethod.execute(sqlSession, args); } //解析执行方法的信息,包括sql语句,参数,返回值等等,放入map中并返回。具体下篇笔记会记录。 private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } } public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { ... case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); //我们重点看这个方法,这里执行的是sqlSessionTempalte的selectOne,那我们看看selectOne具体是怎么实现的 result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; ... } ... return result; } } public class SqlSessionTemplate implements SqlSession, DisposableBean { @Override public <T> T selectOne(String statement, Object parameter) { //是通过sqlSessionProxy执行selectOne,前面我们已经分析过sqlSessionProxy,代理的主要作用就用于执行操作后关闭session return this.sqlSessionProxy.selectOne(statement, parameter); } }
最后总结一下,Mapper实例化的主要流程就是:
MapperScannerRegistrar 注册 ClassPathMapperScanner -->
ClassPathMapperScanner 扫描 @MapperScan 路径下配置的所有接口,并设置类型为MapperFactoryBean,同时设置自动注入 -->
MapperFactoryBean 实例化时,会调用setSqlSessionFactory实例化sqlSessionTemplate-->
getBean获取mapper时,会创建一个创建一个代理对象-->
调用mapper方法时,会调用MapperMethod.invoke(jdk动态代理),最终调用哪个sqlSessionTemplate中sqlSessionProxy的方法,然后关闭session。
以上是关于mybaits-spring 源码阅读随笔的主要内容,如果未能解决你的问题,请参考以下文章