第一步:首先看看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();
}
整个过程大致如图:
如有不对请告知,帮忙纠正。