本文共 8013 字,大约阅读时间需要 26 分钟。
说一下写这篇文章的目的,是为了帮助大家梳理一下在spring环境下我们需要做哪些工作去集成mybatis,以及为什么这么去做。好了下面进入我们的本次议题。
首先说一下mybatis的几个关键核心类,及他们的之间的关系。第一个SqlSessionFactory,用来创建SqlSession接口的实例的,默认是DefaultSqlSessionFactory类型的对象。第二个是SqlSession,通过他我们可以管理Connection和获取mapper 映射器实例的动态代理对象,从而进行dao 查询.到此基础的核心类就介绍到这。
接着说一下和spring集成时的一些操作
或者开启包扫描
@Configuration@MapperScan("org.mybatis.spring.sample.mapper")public class AppConfig { // ...}
好,最后我们总结一下spring集成mybatis所做的两个操作:1 创建sqlSessionFactoryBean 2 通过MapperBeanFactory或自动扫描创建mapper.那么@MapperScan 这个注解为我们做了什么操作,就能够将mapper代理对象放到ioc容器中去呢?
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Import({MapperScannerRegistrar.class})//引入这个类public @interface MapperScan { String[] value() default {}; String[] basePackages() default {}; Class [] basePackageClasses() default {}; Class nameGenerator() default BeanNameGenerator.class; Class annotationClass() default Annotation.class; Class markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; Class factoryBean() default MapperFactoryBean.class;}
//mybatis version : 1.3.1.jarpublic class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { private ResourceLoader resourceLoader; public MapperScannerRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); //此处进行包的扫描 scanner.doScan(StringUtils.toStringArray(basePackages)); } public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; }}
public SetdoScan(String... basePackages) { Set beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else {//这个这个函数的关键位置 this.processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
private void processBeanDefinitions(SetbeanDefinitions) { Iterator var3 = beanDefinitions.iterator(); while(var3.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next(); GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition(); if (this.logger.isDebugEnabled()) { this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); }//将我们原有的bean信息中class 作为构造函数参数传入构造函数 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); //将我们扫描到的mapper的bean的信息中class 的信息替换为 mapperFactoryBean.class 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) { this.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) { this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { if (this.logger.isDebugEnabled()) { this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } //并将这个bean设置为属性自动注入,这摸做的目的是将sessionfactory 注入进去 definition.setAutowireMode(2); } }
现在我们容器中的mapper接口现在已经替换为了MapperFactoryBean了。既然他是一个FactoryBean,我们必然会调用.getObject()方法来获取mapper,那么我们在来看看MapperFactoryBean中做了什么?
public T getObject() throws Exception {//这通过 sqlSession 进行mapper 动态代理的创建,(1)但是sqlSession 是谁创建的呢?(2)默认的sqlSession并不是线程安全的,每个mapper一个mapperFactorybean会不会造成线程不安全问题呢? return this.getSqlSession().getMapper(this.mapperInterface); }
此时我们在关联上上面的设置自动注入属性就会清晰很多了。
//sqlSessionFactory属性注入时 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) {//创建一个SqlSessionTemplate ,我这边需要说明一下,他实现了sqlSession接口,并且它是线程安全的 this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } }
到此为止,spring集成mybatis的所有代码流程梳理就介绍完了。
我们来在总结一下:
1) 添加注解@MapperScan时引入MapperScannerRegistrar
2)在MapperScannerRegistrar 中调用doScan获取扫描包的信息
3) doScan调用processBeanDefinitions()方法
4)在这里面将扫描的Mapper接口的BeanDefinition的信息进行篡改 ,class设置为mapperFactoryBean ,构造函数参数设置为mapper接口名称,设置自动属性注入
5) 自动属性注入时,会注入sqlSessionFctory时创建一个线程安全的sqlSession
6)当我们getBean获取mapper时,会通过getObject方法通过sqlSession.getMapper()方法获取我们所需要的动态代理对象
ok,这个MyBatis-spring集成的东西就这样吧。下面我们在来看看springBoot中是如何做的。
//mybatis-spring-boot-autoconfigure version 1.3.0.jar@Configuration//mybatis的属性对象封装类,并放入我们bean中@EnableConfigurationProperties({MybatisProperties.class})public class MybatisAutoConfiguration { ... ... ...//创建SqlSessionFactory @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); ... return factory.getObject(); }//创建SqlSessionTemplate @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory); } @Configuration//引入AutoConfiguredMapperScannerRegistrar @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class}) @ConditionalOnMissingBean({MapperFactoryBean.class}) public static class MapperScannerRegistrarNotFoundConfiguration { ...这里面没有什么卵用 } public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware { private BeanFactory beanFactory; private ResourceLoader resourceLoader; public AutoConfiguredMapperScannerRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper"); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); //这又是之前我们的逻辑了 scanner.doScan(StringUtils.toStringArray(packages)); } }}
转载地址:http://ozelf.baihongyu.com/