spring加载mybatis启动流程

第一步:首先看看mybatis在spring里的两个关键配置项

	<!-- 1.配置sqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="WEB-INF/mybatis-config.xml" />
		<property name="dataSource" ref="dataSource" />
		<!-- 自动扫描mapping.xml文件 -->
		<property name="mapperLocations" value="classpath*:com/czl/**/mybatis/*.xml" />                    
    </bean>
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">    
        <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>    
    </bean>

	<!-- 2.配置mapper接口的扫描类 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
            p:basePackage="com.test.**.mapper"
            p:sqlSessionFactoryBeanName="sqlSessionFactory" />

1.配置sqlSessionFactory,用来生成sqlSessionFactory。

2.扫描mapper接口的类,后面做详解。

第二步:启动项目看加载过程

1. 第1步进入方法:org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSet。因为SqlSessionFactoryBean有继承接口InitializingBean。

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

buildSqlSessionFactory()会解析mybatis的配置信息封装到Configuration里,Configuration是mybatis里的一个重要组件,里面包含了几个重要组件,以下是部分组件:

    environment:环境。

    executor:封装了jdbc,执行sql用。

    mappedStatements:封装了sql的id和sql语句的map映射。

方法最后会返回一个sqlSessionFactory并且注入bean容器。

2. 第2步执行扫描类MapperScannerConfigurer。

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

因为该类有继承接口BeanDefinitionRegistryPostProcessor,spring容器启动就会执行到该方法。

该方法扫描mybatis配置的mapper接口,并注入到spring的BeanDefinition里面,BeanDefinition里的targetType=MapperFactoryBean,spring在实例化一个bean(比如**serviceBean)的时候解析到依赖的mapper接口,把mapper转换成 MapperFactoryBean,然后调用MapperFactoryBean.getObject(), 最后会经过MapperProxyFactory动态代理完成初始化。

为什么会调用MapperFactoryBean.getObject()?因为MapperFactoryBean继承了接口FactoryBean,spring的加载机制会调用FactoryBean的getObject()。

spring启动加载mybatis的工作至此就已经完成了。

第三步:执行阶段

mybatis有三种执行方式

第1种:通过sqlSession执行sql

this.getSqlSession()会获取到第一步里在bean里注入的sqlSession,我这里sqlSession的实现类是SqlSessionTemplate

	/** 
	 * @param id
	 */
	public void selectOne(String id) {
		this.getSqlSession().selectOne("com.test.mapper.testMapper.selectOneTest" + , id);
	}

SqlSessionTemplate里的伪代码如下:

public class SqlSessionTemplate implements SqlSession, DisposableBean {

	private final SqlSessionFactory sqlSessionFactory;

	private final ExecutorType executorType;

	private final SqlSession sqlSessionProxy;

	private final PersistenceExceptionTranslator exceptionTranslator;

	public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
	}

	public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
		this(sqlSessionFactory, executorType,
				new MyBatisExceptionTranslator(
						sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
	}

   /**
   * sqlSessionProxy从这里生成
   */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

	
    /**
    * 调用这个查询
    */
	@Override
	public <T> T selectOne(String statement, Object parameter) {
		return this.sqlSessionProxy.<T>selectOne(statement, parameter);
	}
}

sqlSessionProxy是调用java.lang.reflect.Proxy.newProxyInstance动态代理生成的。

第2种使用mapper接口调用

@Service
public class AddressService implements IAddressService {

	/**
	 * dao
	 */
	@Autowired
	public IAddressMapper addressMapper;
	
	@Override
	public List<AddressesEntity> listAddress(String addressId) {
		List<AddressesEntity> list = this.addressMapper.listAddress(addressId);
		return list;
	}

}
public interface IAddressMapper {
	List<AddressesEntity> listAddress(String addressId);
}

IAddressMapper是mapper接口,在第一步里有经过配置扫描,被注入到spring的beanDefinition里,

spring在实例化AddressService的时候会解析依赖的addressMapper,会使用动态代理实例化成MapperProxy类。MapperProxy里面有sqlSession执行sql。

第3种是在mapper接口的方法上的注解写sql,这种方式几乎不用。

public interface IAddressMapper {
    @Select("select * from Address")
    List<Address> listAddress();
}

整个过程大致如图:

如有不对请告知,帮忙纠正。

猜你喜欢

转载自blog.csdn.net/qq_33743572/article/details/106478041