MapperScan的工作,Spring-Mybatis怎么自动getMapper

关山初度尘未洗,策马扬鞭再奋蹄!这篇文章主要讲述MapperScan的工作,Spring-Mybatis怎么自动getMapper相关的知识,希望能为你提供帮助。
@MapperScan

@Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { ... }

导入了MapperScannerRegistrar,这里面registerBeanDefinitions方法中new了一个ClassPathMapperScanner,调用doScan方法进行spring包扫描,会返回对应的BeanDefinition。
//ClassPathMapperScanner @Override public Set< BeanDefinitionHolder> doScan(String... basePackages) { Set< BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> "No MyBatis mapper was found in ‘" + Arrays.toString(basePackages) + "‘ package. Please check your configuration."); } else { processBeanDefinitions(beanDefinitions); }return beanDefinitions; }

在ClassPathMapperScanner中doScan会调用父类的doScan方法,这里面会通过BeanDefinition注册Bean
registerBeanDefinition(definitionHolder, this.registry);

在ClassPathMapperScanner的processBeanDefinitions中会设置BeanDefinition的构造参数,BeanClass等属性。
到现在为止,Spring的BeanFactory中已经有了很多MapperFactoryBean(每一个都是不同的Mapper接口类型),然后MapperFactoryBean继承了DaoSupport,在afterPropertiesSet方法中会执行checkDaoConfig();
@Override public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException { // Let abstract subclasses check their configuration. checkDaoConfig(); // Let concrete implementations initialize themselves. try { initDao(); } catch (Exception ex) { throw new BeanInitializationException("Initialization of DAO failed", ex); } }

在MapperFactoryBean的checkDaoConfig方法中,会注册Mapper到Configuration的mapperRegistry中。
//MapperFactoryBean @Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property ‘mapperInterface‘ is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig & & !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper ‘" + this.mapperInterface + "‘ to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } }

然后从容器中拿Mapper的Bean的时候,实际调用MapperFactoryBean的getObject方法,会拿到对应得Mapper。
//MapperFactoryBean @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }

后面就是Mybatis中的内容,会通过MapperProxyFactory new 一个MapperProxy(InnovationHandler),然后里面有MapperMethod的逻辑。
这里走Mybatis的逻辑。
//DefaultSqlSession @Override public < T> T getMapper(Class< T> type) { return configuration.< T> getMapper(type, this); }

//Configuration public < T> T getMapper(Class< T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }

//MapperRegistry public < T> T getMapper(Class< T> type, SqlSession sqlSession) { final MapperProxyFactory< T> mapperProxyFactory = (MapperProxyFactory< T> ) 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); } }

MapperFactoryBean这中间还有封装,MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport中装饰了SqlSessionTemplate.(SqlSessionTemplate代理了SqlSession)
【MapperScan的工作,Spring-Mybatis怎么自动getMapper】SqlSessionTemplate装饰了SqlSessionFactory,通过SqlSessionFactory,Proxy newInstance了一个SqlSession实例(sqlSessionProxy),使用了SqlSessionInterceptor(InnovationHandler)这个代理。在这里面进行SQLSession的管理,然后在给SQLSession本身执行。
SqlSessionTemplateMybatisAutoConfiguration 注入了SqlSessionTemplate Bean在IOC容器中。
SqlSessionTemplate 实际上是Spring对于SqlSession的管理,自动创建,自动关闭。调用SqlSessionTemplate 的方法实际上是调用sqlSessionProxy[SqlSessionInterceptor为InnovationHandler]的方法。
//SqlSessionTemplate @Override public int delete(String statement) { return this.sqlSessionProxy.delete(statement); }/** * {@inheritDoc} */ @Override public int delete(String statement, Object parameter) { return this.sqlSessionProxy.delete(statement, parameter); }

//MybatisAutoConfiguration @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); if (executorType != null) { return new SqlSessionTemplate(sqlSessionFactory, executorType); } else { return new SqlSessionTemplate(sqlSessionFactory); } }

在SqlSessionInterceptor中会自动创建sqlSession,自动关闭sqlSession。
SqlSessionInterceptor会尝试获取当前线程的sqlsession,如果没有就使用sqlsessionfactory创建。
//SqlSessionTemplate 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); } } }


    推荐阅读