假如不结合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作了一些自定配置等。
分析的有点乱,先这样吧~