04.MyBatis 集成 Spring 原理

1. xml 集成原理

1.1 xml 配置
<!-- 自动的扫描所有的 mapper 的实现并加入到 ioc 容器中 -->
<bean id="configure" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--basePackage:指定包下所有的 mapper 接口实现自动扫描并加入到 ioc 容器中-->
    <property name="basePackage" value="pers.mangseng.study.mybatis.mapper"/>
</bean>
1.2 xml 原理
1.2.1 MapperScannerConfigurer#postProcessBeanDefinitionRegistry
  • org.mybatis.spring.mapper.MapperScannerConfigurer
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
  • 由于实现了 BeanDefinitionRegistryPostProcessor 接口,所以重点关注 postProcessBeanDefinitionRegistry 方法
@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);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

2. 注解集成原理

2.1 注解配置
@Configuration
// 配置 mybatis 扫描注解包
@MapperScan(basePackages = "pers.mangseng.study.mybatis.mapper")
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisConfig {
2.2 注解原理
2.2.1 MapperScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 给容器中导入 MapperScannerRegistrar 组件     ------> 2.2.2
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
2.2.2 MapperScannerRegistrar#registerBeanDefinitions
  • org.mybatis.spring.annotation.MapperScannerRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // 获取 @MapperScan 的注解信息
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    ...

    List<String> basePackages = new ArrayList<String>();
    
    // 解析 value 属性,因为 value 同 basePackages,添加到 basePackages 中
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    
    // 解析 basePackages 属性,添加到 basePackages 中
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    
    // 解析 basePackageClasses 属性,添加到 basePackages 中
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    
    // 扫包     ------> 2.2.3
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    // 注入 resourceLoader
    this.resourceLoader = resourceLoader;
  }

}
  • 由于实现了 ImportBeanDefinitionRegistrar 接口,所以重点关注 registerBeanDefinitions 方法

    refresh()
        invokeBeanFactoryPostProcessors(beanFactory)
            PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)
                    postProcessor.postProcessBeanDefinitionRegistry(registry)
                        processConfigBeanDefinitions(registry)
                            this.reader.loadBeanDefinitions(configClasses)
                                loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator)
                                    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())
                                        registrar.registerBeanDefinitions(metadata, this.registry)
    
2.2.3 ClassPathMapperScanner#doScan
  • org.mybatis.spring.mapper.ClassPathMapperScanner
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 调用 spring 进行扫包     ------> 2.2.4
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      // 调用 processBeanDefinitions 方法给 bean 添加 MyBatis 的功能,否则只是普通的 spring bean        ------> 2.2.5
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}
2.2.4 ClassPathBeanDefinitionScanner#doScan
  • org.springframework.context.annotation.ClassPathBeanDefinitionScanner
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	for (String basePackage : basePackages) {
	    // 组装 BeanDefinition
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				// 添加到 beanDefinitions 后返回
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}
2.2.5 ClassPathMapperScanner#processBeanDefinitions
  • org.mybatis.spring.mapper.ClassPathMapperScanner
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      ...

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      
      // 设置 bean 的 class 类型,为 MapperFactoryBean
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      ...
      
    }
}
2.3 事务原理
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MybatisConfig.class);
    // 调用 getBean 获取对象        ------> 2.3.1
    EmployeeMapper employeeMapper = context.getBean(EmployeeMapper.class);
    System.out.println(employeeMapper);
    // 调用查询     ------> 2.3.4
    Employee employee = employeeMapper.getEmpById(1);
    System.out.println(employee);
}
2.3.1 AbstractApplicationContext
  • org.springframework.context.support.AbstractApplicationContext#getBean
context.getBean(EmployeeMapper.class)
    getBean(requiredType)
        getBean(requiredType, (Object[]) null)
            resolveBean(ResolvableType.forRawClass(requiredType), args, false)
                resolveNamedBean(requiredType, args, nonUniqueAsNull)
                    new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args))
                        doGetBean(name, requiredType, args, false)
                            getObjectForBeanInstance(sharedInstance, name, beanName, null)
                                super.getObjectForBeanInstance(beanInstance, name, beanName, mbd)
                                    getObjectFromFactoryBean(factory, beanName, !synthetic)
                                        doGetObjectFromFactoryBean(factory, beanName)
                                            // factory 是 MapperFactoryBean     ------> 2.3.2
                                            factory.getObject()
2.3.2 MapperFactoryBean
  • org.mybatis.spring.mapper.MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    //intentionally empty 
  }
  
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  ...

  @Override
  public T getObject() throws Exception {
    // 返回 mapper 的代理对象
    // getSqlSession 实际获取到的是 sqlSessionTemplate      ------> 2.3.3
    return getSqlSession().getMapper(this.mapperInterface);
  }

  @Override
  public Class<T> getObjectType() {
    // 返回 classType
    return this.mapperInterface;
  }

  @Override
  public boolean isSingleton() {
    // 单例
    return true;
  }

  ....

}
2.3.3 SqlSessionDaoSupport#getSqlSession
  • org.mybatis.spring.support.SqlSessionDaoSupport

  • spring 在调用 populateBean 方法给 bean 赋值时候,会通过反射调用 set 方法,因此会调用 setSqlSessionFactorysetSqlSessionTemplate 方法

public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSession sqlSession;

  private boolean externalSqlSession;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      // 给 sqlSession 赋值,sqlSessionTemplate 是 sqlSession 的实现类
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    // 给 sqlSession 赋值,sqlSessionTemplate 是 sqlSession 的实现类
    this.sqlSession = sqlSessionTemplate;
    this.externalSqlSession = true;
  }

  public SqlSession getSqlSession() {
    // 返回 sqlSessionTemplate
    return this.sqlSession;
  }

  ...

}
2.3.4 MapperMethod#execute
  • org.apache.ibatis.binding.MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      // 判断增删改查标签
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        // 根据返回值的类型调用不同的方法
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          // 参数转换
          Object param = method.convertArgsToSqlCommandParam(args);
          // 调用查询方法,注意此时的 sqlSession 是 sqlSessionTemplate     ------> 2.3.5
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
}
2.3.5 SqlSessionTemplate#selectOne
  • org.mybatis.spring.SqlSessionTemplate
@Override
public <T> T selectOne(String statement, Object parameter) {
    // 重载     ------> 2.3.6
    return this.sqlSessionProxy.<T> selectOne(statement, parameter);
}
2.3.6 SqlSessionInterceptor#invoke
  • org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 每次都会获取一个新的 sqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        // 如果没有被 transactional 管理,如果被 aop 代理,则通过 spring 的事务支持来控制
        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        ------> 2.3.7
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
}
2.3.7 SqlSessionUtils#closeSqlSession
  • org.mybatis.spring.SqlSessionUtils
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    
    // 获取当前 holder 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    if ((holder != null) && (holder.getSqlSession() == session)) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
      }
      // 如果 holer 不为 null,调用 released() 方法     ------> 2.3.8
      holder.released();
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
      }
      session.close();
    }
}
2.3.8 ResourceHolderSupport#released
  • org.springframework.transaction.support.ResourceHolderSupport
public void released() {
    // 持有的引用 -1,并没有去关闭 session
	this.referenceCount--;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值