Spring整合mybatis源码阅读笔记

96 阅读2分钟

使用xml方式启动spring

bean.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
       <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
       <property name="url" value="jdbc:mysql://localhost/test" />
       <property name="username" value="root" />
       <property name="password" value="123456" />
    </bean>
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
       <property name="dataSource" ref="dataSource" />
       <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
       <property name="basePackage" value="org.spring.dao"/>
    </bean>
    <bean id="equipServiceImpl" class="org.spring.serviceImpl.EquipServiceImpl">
       <property name="equipMapper" ref="equipMapper"/>
    </bean>
    <context:component-scan base-package="org.spring.dao" />
</beans>

启动流程

1. MapperScannerConfigure扫描所有定义在basePackage下的接口

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
    
    该类实现了BeanDefinitionRegistryPostProcessor接口,作为bean定义的后置处理,在刷新容器时添加bean定义
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }

  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);
  // 设置扫描到的bean calss为MapperFactoryBean
  scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  if (StringUtils.hasText(lazyInitialization)) {
    scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  }
  if (StringUtils.hasText(defaultScope)) {
    scanner.setDefaultScope(defaultScope);
  }
  scanner.registerFilters();
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
在bean定义的后置处理方法中 核心就是扫描 base-package包中所有的接口,注意扫描后的bean定义class为MapperFactoryBean

2、sqlSessionFactoryBean对象创建

image.png sqlSessionFactoryBean对象实现FactoryBean、initializingBean接口

@Override
public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }

  return this.sqlSessionFactory;
}
返回sqlSessionFactory对象,spring在使用getBean("sqlSessionFactoryBean")获取对象时返回的都是sqlSessionFactory对象
@Override
public void afterPropertiesSet() throws Exception {
  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");

  this.sqlSessionFactory = buildSqlSessionFactory();
}
sqlSessionFactoryBean利用sprngbean的生命周期的该方法,创建sqlSessionFactory对象,并且添加为属性

SqlSessionFactoryBean类 image.png Configuration类 image.png MapperRegister类 image.png SqlSessionFactoryBean中有一属性configuration,configuration中有MapperRegister属性,而MapperRegister中就是已知的mapper接口和其应的Mapper代理工厂对象 注:sqlSessionFactoryBean对象的创建期间,会将各个mapper接口和其应的Mapper代理工厂对象进行保存

3、创建equipMapper对象

正常创建MapperFactoryBean类型的equipMapper对象

4、getBean("equipMapper")

spring在getBean("equipMapper")时,因为equipMapper是工厂对象,会返回MapperFactoryBean的getObject方法返回的对象

MapperFactoryBean
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

DefaultSqlSession
@Override
public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}
此时的configuration就是SqlSessionFactoryBean的configuration

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);
  }
}
调用在创建sqlSessionFactory时保存的mapper代理工厂,直接newInstance返回mapper代理类

补充:mapper和sqlsession的关系,即同一线程调用mapper执行方法时使用同一个sqlsession