mybatis学习笔记(下)

简介: Mybatis与spring集成时,在spring.xml中会配置两个重要的类,SqlSessionFactoryBean与MapperScannerConfigure。用于完成mybatis的加载与配置。

在xml文件解析完成之后,通过sqlSessionFactoryBuilder构造SqlSessionFactory对象

this.sqlSessionFactoryBuilder.build(configuration);

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


这样就完成了SqlSessionFactory创建。


四、MapperScannerConfigurer介绍


<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.rsms.iot.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>


MapperScannerConfigure的postProcessBeanDefinitionRegistry方法中,会通过ClassPathMapperScanner扫描basePackage下的所有接口。

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));
}


ClassPathMapperScanner中通过doScan将原接口类型改造成MapperFactoryBean。为每个接口类创建动态代理。可以从下面源码看出

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    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(beanDefinitions);
    }
    return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }
      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      // mapper接口是原bean类型,但是实际类型是MapperFactoryBean
      definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
      definition.setBeanClass(this.mapperFactoryBean.getClass());
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
      ......
    }
}


上面代码可以看出MapperScannerConfigure是为了避免一个一个定义MapperFactoryBean而添加的批量处理mapper接口的方法,根据basepackages路径,将mapper接口批量改造成MapperFactoryBean,但是元数据中依然保存了原接口类型信息,可以从下图看出。


image.png

3.png


五、MapperFactoryBean的装配


上一节中我们知道mapper接口最后都被改造成MapperFactoryBean,而MapperFactoryBean继承SqlSessionDaoSupport,而SqlSessionDaoSupport又继承InitializingBean,所以所有的MapperFactoryBean最后都会执行afterPropertiesSet完成自动装配。(DaoSupport->afterPropertiesSet()方法)

之后通过MapperFactoryBean的getObject获取代理对象(MapperProxy)注入到spring容器中。我们看看spring是如何获取代理对象的。

MapperFactoryBean类的getObject方法:
public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}
调用SqlSessionTemplate的getMapper方法:
public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
}
接着调用Configuration的getMapper方法:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
再调用MapperRegistry的getMapper方法:
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);
    }
}
通过MapperProxyFactory实例化一个代理对象
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
Proxy.newProxyInstance就是jdk自带的动态代理完成MapperProxy的构造。


完成每个Mapper接口的代理对象的构造之后就会注入到spring容器管理。


六、mapper的调用过程


根据第三节我们知道,一系列mapper结构改造成MapperFactoryBean后进一步构造出其代理对象MapperProxy,所以调用mapper接口方法时,实际是调用MapperProxy的invoke方法,而该方法中又会调用MapperMethod的execute方法。如下代码

MapperProxy的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
}


下面以列表查询为例,看下mybatis的执行过程,当然不同的执行语句调用的Executor是不同的,这里仅供参考。DefaultSqlSession之前,即调用mapper接口时,实际是调用MapperProxy的invoke方法,MapperProxy再调用MapperMethod的execute方法,MapperMethod才会调用DefaultSqlSession。


image.png

查询过程.png


相关文章
|
XML Java 数据库连接
java202304java学习笔记第六十五天-ssm-声明式控制-基于xml的声明式配置-mybatis的概述2
java202304java学习笔记第六十五天-ssm-声明式控制-基于xml的声明式配置-mybatis的概述2
57 0
java202304java学习笔记第六十六天-ssm-mybatis-接口代理方法实现
java202304java学习笔记第六十六天-ssm-mybatis-接口代理方法实现
43 0
|
7月前
|
SQL Java 关系型数据库
MyBatisPlus学习笔记(SpringBoot版)
MyBatisPlus学习笔记(SpringBoot版)
505 0
|
SQL Java 关系型数据库
|
SQL Java 关系型数据库
java202304java学习笔记第六十六天-ssm-mybatis的dao层实现1
java202304java学习笔记第六十六天-ssm-mybatis的dao层实现1
37 0
java202304java学习笔记第六十六天-ssm-mybatis中dao层实现-动态sql-foreach之2
java202304java学习笔记第六十六天-ssm-mybatis中dao层实现-动态sql-foreach之2
59 0
java202304java学习笔记第六十六天-ssm-mybatis中dao层实现-动态sql-if之1
java202304java学习笔记第六十六天-ssm-mybatis中dao层实现-动态sql-if之1
38 0