假如不結合Spring框架,咱們使用MyBatis時的一個典型使用方式以下:java
public class UserDaoTest { private SqlSessionFactory sqlSessionFactory; @Before public void setUp() throws Exception{ ClassPathResource resource = new ClassPathResource("mybatis-config.xml"); InputStream inputStream = resource.getInputStream(); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void selectUserTest(){ String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}"; SqlSession sqlSession = sqlSessionFactory.openSession(); CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class); Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id); System.out.println(cbondissuer); sqlSession.close(); } }
咱們首先須要SqlSessionFactory,而後經過SqlSessionFactory的openSession方法得到SqlSession。經過SqlSession得到咱們定義的接口的動態代理類(MapperProxy)。當咱們整合Spring框架時,咱們使用MyBatis的方式簡單的「不可思議」:spring
@Autowore private CbondissuerMapper cbondissuerMapper;
很是Spring形式的使用方式,直接注入就能夠了。那這個是怎麼實現的呢?Spring是怎麼把上面略顯複雜的模板代碼省略的呢?個人第一直覺是Spring在啓動的時候作了Mybatis的初始化工做,而後一次性獲取了全部Mapper接口的動態代理實現類並將其放入Spring容器中進行管理。sql
下面來驗證下本身的猜測對不對。mybatis
下面以Spring Boot中的MyBatisAutoConfigration爲列子作下簡單分析:app
//SqlSessionFactoryBean的最終做用就是解析MyBati配置文件,並最終生成Configration對象,而後生成DefaultSqlSessionFactory,並加入Spring的容器管理。能夠看出SqlSessionFactoryBean的做用和SqlSessionFactoryBeanBuilder的做用很像。 @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } Configuration configuration = this.properties.getConfiguration(); if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new Configuration(); } if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { for (ConfigurationCustomizer customizer : this.configurationCustomizers) { customizer.customize(configuration); } } factory.setConfiguration(configuration); if (this.properties.getConfigurationProperties() != null) { factory.setConfigurationProperties(this.properties.getConfigurationProperties()); } if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } if (this.databaseIdProvider != null) { factory.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } return factory.getObject(); } //建立SqlSessionTemplate這個Bean,這個類動態代理了DefaultSqlSession,因此最後仍是調用了DefaultSqlSession,下面會重點分析下SqlSessionTemplate @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); } }
如下是SqlSessionTemplate的源代碼,因爲源碼較多,只貼出重點部分:框架
public class SqlSessionTemplate implements SqlSession, DisposableBean { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; //SqlSessionTemplate動態代理了DefaultSqlSession,所用對sqlSessionProxy的調用都會通過代理對象 private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; .... 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的CRUD調用都會先調用到這裏 private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //這步至關於調用sqlSessionFactory的openSesion方法得到SqlSession SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { //調用DefaultSqlSession的CRUD方法 Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { 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) { //最後強制關閉SqlSession closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } } }
到此爲止,整個Spring中獲取SqlSessionFactory、得到SqlSession、執行CRUD、以及關閉SqlSession的流程都進行分析了。還有一個有疑問的地方就是Mapper接口的實現類是在何時動態生成的。ide
咱們知道MapperScan是用來掃描Mapper接口的,因此很天然的想到去MapperScan這個類裏面一探究竟。ui
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented //祕密在MapperScannerRegistrar裏面 @Import(MapperScannerRegistrar.class) public @interface MapperScan { String[] value() default {}; //設置掃描的包 String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends Annotation> annotationClass() default Annotation.class; Class<?> markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; //指定自定義的MapperFactoryBean Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class; }
下面就看下MapperScannerRegistrar裏面作了什麼:this
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); //建立了一個ClassPathMapperScanner,並根據@MapperScan裏面的配置設置屬性 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); // this check is needed in Spring 3.1 if (resourceLoader != null) { scanner.setResourceLoader(resourceLoader); } Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { scanner.setAnnotationClass(annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { scanner.setMarkerInterface(markerInterface); } Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass)); } Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass)); } scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); List<String> basePackages = new ArrayList<String>(); for (String pkg : annoAttrs.getStringArray("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : annoAttrs.getStringArray("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } scanner.registerFilters(); //開始掃描 scanner.doScan(StringUtils.toStringArray(basePackages)); }
以上建立了一個ClassPathMapperScanner,並根據@MapperScan裏面的配置設置屬性,開始掃描。下面再看ClassPathMapperScanner。debug
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; //對掃描到的BeanDefinition作進一步處理 for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 //這邊是重點,將掃描到的Mapper接口的BeanClass設置爲MapperFactoryBrean //MapperFactoryBrean是工廠Bean,用於生成MapperProxy; //Spring在自動注入Mapper的時候會自動調用這個工廠Bean的getObject方法,生成MapperProxy並放入Spring容器。 definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { if (logger.isDebugEnabled()) { logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } }
到此Spring自動生成Mapper接口實現類的過程也分析完了。
經過以上分析,印證了一開始的猜測:Spring在啓動的時候作了Mybatis的初始化工做,而後一次性獲取了全部Mapper接口的動態代理實現類並將其放入Spring容器中進行管理。大體流程以下:
Mapper接口生成的流程大體以下:
以上大體就是MyBatis整合進Spring的原理。咱們發現其實質和傳統的Mybatis使用是同樣的,只不過是經過Spring作了一些自定配置等。
分析的有點亂,先這樣吧~