MyBatis源码分析

本文详细分析了MyBatis的入口SqlSessionFactoryBean,揭示了其初始化过程,以及配置文件加载和解析的过程。通过配置篇,讲解了mapperLocations属性、XMLMapperBuilder的parse()方法解析XML配置文件,特别是<resultMap>和<sql|select|update|insert|delete>节点的解析。最后在应用篇中,探讨了MyBatis如何通过MapperProxy调用DAO方法,执行SQL并进行结果映射。

篇章一:入口篇

我们学习Mybatis时知道其核心是SqlSessionFactory,它是mybatis的核心类,也是Mybatis运行的入口,spring集成mybatis时需要配置SqlSessionFactoryBean和扫描mapper的MapperScannerConfigurer,spring-mybatis集成主要的配置就这么点,从这理解也就不难理解mybatis入口问题了,但是<bean>节点只是指明了该类的路径和一些属性信息,并没有指明先运行哪个方法呀?我们带着问题【SqlSessionFactoryBean是入口,那入口方法是哪个?】继续研究下去,

<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations"
			value="classpath:com/jianlejun/mapper/*.xml" />
		<property name="configuration">
			<bean class="org.apache.ibatis.session.Configuration">
				<property name="mapUnderscoreToCamelCase" value="true" />
			</bean>
		</property>
	</bean>
	<!--扫描dao -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.jianlejun.dao" />
		<property name="sqlSessionFactoryBeanName"
			value="sqlSessionFactory"></property>
</bean>

 我们点进去看看SqlSessionFactoryBean.

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

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

@Override
  public void onApplicationEvent(ApplicationEvent event) {
    if (failFast && event instanceof ContextRefreshedEvent) {
      // fail-fast -> check all statements are completed
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
  }
}

 可以看到SqlSessionFactoryBean实现了三个接口:

1.ApplicationListener接口:里面只有一个onApplicationEvent方法,有什么用呢?看官方的注释,它是基于观察者模式创建的,当上下文ApplicationContext加载完实现了该接口的bean后,负责通知该bean,bean接收到通知后(onApplicationEvent方法),可以在方法内做自己的逻辑处理,简单点理解就是容器你好,我收到你加载我完毕的通知了,接下里就交给我吧,不用你操心了。

很显然这不是我们要找的方法入口,因为bean都加载完了,说明mybatis配置文件什么都处理完了,那我们再来看看InitializingBean接口

2.InitializingBean接口:接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法(如果bean配置了init-method属性,afterPropertiesSet优先级高于init-method)。

由上可知,这就是我们要找的入口了,spring在初始化bean时,会先调用afterPropertiesSet()方法,从上面代码可知,它在里面调用了buildSqlSessionFactory()方法,哎,最上面我们是不是说了mybatis的核心是SqlSessionFactory,可想而知,这个构造SqlSessionFactory的方法buildSqlSessionFactory()是最核心的方法了。

入口篇完。

篇章二:配置篇

1.配置文件加载

Mybatis配置bean时注入了一个mapperLocations属性,指明项目xml mapper文件的路径,SqlSessionfactoryBean内定义了一个Resource数组接收mapperLocations的位置

<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations"
			value="classpath:com/jianlejun/com/mapper/*.xml" />
		<property name="configuration">
			<bean class="org.apache.ibatis.session.Configuration">
				<property name="mapUnderscoreToCamelCase" value="true" />
			</bean>
		</property>
</bean>
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
  private Resource configLocation;

  private Configuration configuration;

  private Resource[] mapperLocations;
/**
   * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
   * configuration at runtime.
   *
   * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file.
   * This property being based on Spring's resource abstraction also allows for specifying
   * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".
   */
  public void setMapperLocations(Resource[] mapperLocations) {
    this.mapperLocations = mapperLocations;
  }
}

2.读取配置文件

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
//省略了前面次要代码
if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }
}

这段代码主要根据mapperLocation的文件流构造XMLMapperBuilder(解析xml文件的核心类),然后调用XMLMapperBuilder的parse()方法进行xml文件解析,点进去看看parse()方法体

public class XMLM
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值