Spring整合MyBatis源码(一)SqlSession创建

一、Spring+MyBatis配置

下面是一个常见的Spring整合MyBatis的applicationContext.xml配置:

Spring整合MyBatis源码(一)SqlSession创建_第1张图片

SqlSessionFactoryBean:负责解析配置文件,并实例化SqlSessionFactory和创建SqlSession
MapperScannerConfigurer:负责创建Mapper,并且注入到Spring的IOC容器中

下面我们分析SqlSession的创建.

二、SqlSessionFactoryBean

1、结构

继承体系:

Spring整合MyBatis源码(一)SqlSession创建_第2张图片

字段:

public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
  private Resource configLocation;

  private Configuration configuration;

  private Resource[] mapperLocations;

  private DataSource dataSource;

  private TransactionFactory transactionFactory;

  private Properties configurationProperties;

  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

  private SqlSessionFactory sqlSessionFactory;

  //EnvironmentAware requires spring 3.1
  private String environment = SqlSessionFactoryBean.class.getSimpleName();

  private boolean failFast;

  private Interceptor[] plugins;

  private TypeHandler[] typeHandlers;

  private String typeHandlersPackage;

  private Class[] typeAliases;

  private String typeAliasesPackage;

  private Class typeAliasesSuperType;

  //issue #19. No default provider.
  private DatabaseIdProvider databaseIdProvider;

  private Class vfs;

  private Cache cache;

  private ObjectFactory objectFactory;

  private ObjectWrapperFactory objectWrapperFactory;
  
  // 方法略...
}

SqlSessionFactoryBean继承的3个接口都是与Spring相关的,最重要的就是FactoryBean和InitializingBean接口,这两个接口的实现负责创建SqlSessionFactory。

而类中的字段大部分都是可通过配置文件配置的属性。

2、FactoryBean和InitializingBean实现

FactoryBean接口:

  //先调用afterPropertiesSet,实例化sqlSessionFactory
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }
  // 返回Bean的类型--SqlSessionFactory
  public Class getObjectType() {
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  }
  //使用单例模式
  public boolean isSingleton() {
    return true;
  }

InitializingBean接口:

  public void afterPropertiesSet() throws Exception {
	// 确保dataSource和sqlSessionFactoryBuilder不为空
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");
	//创建sqlSessionFactory
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

可以看到,在获取Bean的时候,首先都会调用 afterPropertiesSet() 实例化sqlSessionFactory,最后由buildSqlSessionFactory()方法完成实例化,下面是完成实例化的代码:

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    Configuration configuration;

	//将配置的config.xml文件解析(如果配置了的话),完成configuration的创建
    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      configuration = this.configuration;
      if (configuration.getVariables() == null) {
        configuration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        configuration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
	  //读取config.xml配置文件,创建Configuration,注意,此时还未对配置文件进行解析
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      // 没有配置文件、使用默认配置
      configuration = new Configuration();
      if (this.configurationProperties != null) {
        configuration.setVariables(this.configurationProperties);
      }
    }
	//--------------------------------------------------

	// 设置objectFactory、objectWrapperFactory、vfs、别名、插件plugins、typeHandlers、databaseIdProvider、cache等属性,略

	//解析配置文件,将配置的属性设置到configuration对象中
    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }
	//创建有Spring管理的transactionFactory
    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }
	//设置Environment属性
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
	
	//如果配置了mapperLocations属性的话,解析它们
    if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }
		//解析mapper.xml,省略try/catch以及log语句
	    XMLMapperBuilder xmlMapperBuilder = 
			new XMLMapperBuilder(mapperLocation.getInputStream(),configuration, mapperLocation.toString(), configuration.getSqlFragments());
        xmlMapperBuilder.parse();
      }
    } else {
      //打印日志,略
    }
	
	//创建sqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

(因为代码较长,省略部分设置属性的代码)

1、实例化SqlSessionFactory的时候,MyBatis首先还是会创建最重要的Configuration对象,该对象包含了MyBatis的所有配置信息,是非常重要的一个类,我们编写的config.xml配置文件就是为了覆盖它的默认配置。可以看到,如果没有编写配置文件的话,MyBatis会使用它的默认Configution。

2、设置objectFactory、objectWrapperFactory、vfs、别名、插件plugins、typeHandlers、databaseIdProvider、cache等属性,这些属性大部分都是用户可以对MyBatis进行拓展、自定义功能的类,略

3、解析config.xml配置文件,将我们的配置设置到Configuration中,下图是解析步骤:

Spring整合MyBatis源码(一)SqlSession创建_第3张图片

4、设置事务相关的transactionFactory和全局相关的Environment属性

5、解析我们配置的mapperLocations属性,并扫描包中的mapper

6、创建SqlSessionFactory--传入Configuration对象,实例化DefaultSqlSessionFactory

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

至此,SqlSessionFactoryBean的任务就已经结束了,下面继续分析MapperScannerConfigurer

三、MapperScannerConfigurer

在第一部分MapperScannerConfigurer的配置中,我们需要设置sqlSessionFactory或者sqlSessionFactoryBeanName属性,让Spring在创建MapperScannerConfigurer后,将上一步创建好的SqlSessionFactory注入到其中。而另一项必需的属性就是basePackage属性,该属性是我们Mapper接口的位置

1、Spring相关的接口

MapperScannerConfigurer的类定义为:

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware 

其中最重要的接口是 BeanDefinitionRegistryPostProcessor 接口,该接口的实现了将Mapper接口的代理类添加到了Spring容器。

下面是实现:

  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }
	//由ClassPathMapperScanner类扫描配置的basePackage,将其中的mapper加入到Spring中
	//设置我们配置的属性
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
    

创建ClassPathMapperScanner来扫描mapper,scan方法实现如下:

	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
            //委托到了doScan方法
		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

真正进行扫描的工作放到了doScan方法中:

  public Set doScan(String... basePackages) {
	//调用父类方法,将package中的mapper属性都封装到 BeanDefinition中
    Set beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
	  //对 beanDefinitions 进行修改
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

doScan方法中,首先调用父类的doScan方法,将basePackage中的mapper接口扫描一遍,将mapper类的信息都封装到 BeanDefinition 中,然后通过修改 BeanDefinition的信息,达到将mapper添加到容器的目的。

  private void processBeanDefinitions(Set beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      //将mapper接口的class属性设置为MapperFactoryBean,并且添加构造函数的参数为mapper的类名
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
	  //将sqlSessionFactory添加到属性中,这样Spring创建MapperFactoryBean的时候,就可以动态的注入sqlSessionFactory
      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;
      }
	  //-------------------------------------------

	  //设置mapper是通过类型注入
      if (!explicitFactoryUsed) {
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

可以看到,mapper的BeanDefinition被动态的修改了:

1、类型被修改为了 MapperFactoryBean

2、添加了一个构造函数的参数--mapper的类名

3、添加了SqlSessionFactory的引用,这样Spring在创建MapperFactoryBean的时候,可以将SqlSessionFactory注入进去

接下来我们看下MapperFactoryBean的实现

2、MapperFactoryBean实现

1、定义结构

Spring整合MyBatis源码(一)SqlSession创建_第4张图片

因为在BeanDefinition中,添加了一个构造函数的参数(mapper的className),所以Spring在实例化MapperFactoryBean的时候会传入mapper的class:

  public MapperFactoryBean(Class mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

2、FactoryBean实现

因为MapperFactoryBean继承了FactoryBean结构,所以它是一个创建Bean的工厂类,真正注入到Spring IOC中的Bean在getObject方法中:

  //通过SqlSession获取mapper
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  //FactoryBean生产的类型就是mapper类型
  public Class getObjectType() {
    return this.mapperInterface;
  }
  //单例
  public boolean isSingleton() {
    return true;
  }

至此,Spring完成了Mapper放入IOC的功能,这样,我们编写代码的时候,可以直接使用Sprng的方式来注入mapper

3、SqlSessionDaoSupport实现

SqlSessionDaoSupport通过sqlSession字段维护了一个SqlSessionTemplate对象,并提供getSqlSession供子类获取该SqlSessionTemplate对象使用。该SqlSessionTemplate是其初始化时传入的SqlSessionFactory创建的:

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

  public SqlSession getSqlSession() {
    return this.sqlSession;
  }

 

 

 

 

 

 

 

 

你可能感兴趣的:(源码,MyBatis,spring)