依赖: mybatis-spring
Mybatis-spring 用于帮助你将 MyBatis 代码无缝地整合到 Spring 中。
Mybatis-spring 兼容性
MyBatis-Spring要求Java5及以上版本还有下面列出的MyBatis和Spring版本:
MyBatis-Spring | MyBatis | Spring |
---|---|---|
1.0.0 或 1.0.1 | 3.0.1 到 3.0.5 | 3.0.0 或以上 |
1.0.2 | 3.0.6 | 3.0.0 或以上 |
1.1.0 | 3.1.0 或以上 | 3.0.0 或以上 |
准备spring项目一个
在pom文件中添加mybatis-spring的依赖
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.0version>
dependency>
3. 配置SqlSessionFactoryBean
4. 配置MapperScannerConfigurer
分析源码之前也需要源码下载并安装到本地仓库和开发工具中,方便给代码添加注释;安装过程和mybatis源码的安装过程是一样的,这里就不再重复描述了;下载地址:https://github.com/mybatis/spring
在 MyBatis-Spring 中, SqlSessionFactoryBean 是用于创建 Sql SessionFactory 的。
mybatis中有两种配置, 一种是只有一个的config setting配置, 另一种是由n个的接口对应的mapper文件
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.len.mybatis.entity"/>
<property name="mapperLocations" value="classpath:sqlmapper/*.xml"/>
bean>
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean,
ApplicationListener<ApplicationEvent> {
@Override
//在spring容器中创建全局唯一的sqlSessionFactory
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();
}
// 将SqlSessionFactory对象注入spring容器
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
<bean id="tUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface"
value="com.len.mybatis.mapper.TUserMapper">property>
<property name="sqlSessionFactory" ref="sqlSessionFactory">property>
bean>
public class MapperFactoryBean<T> extends SqlSessionDaoSupport
implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
通过 MapperScannerConfigurer类自动扫描所有的 Mapper 接口,使用时可以直接注入接口 。
MapperScannerConfigurer中常配置以下两个属性 。
MapperScannerConfigurer和上面的MapperFactoryBean作用是一样的, 都是为了将对象放入容器中
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.len.mybatis.mapper"/>
bean>
BeanDefinitionRegistryPostProcessor: 这个后置处理器具有修改bean的功能, 也只有这个后置处理器可以修改bean, 因为只有这个可以拿到BeanDefinition
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor,
InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {//占位符处理
processPropertyPlaceHolders();
}
//实例化ClassPathMapperScanner,并对scanner相关属性进行配置
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();//根据上述配置,生成过滤器,只扫描合条件的class
//扫描指定的包以及其子包
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
@Override
public void afterPropertiesSet() throws Exception {
notNull(this.basePackage, "Property 'basePackage' is required");
}
}
org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//通过父类的扫描,获取所有复合条件的BeanDefinitionHolder对象
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 {
//处理扫描得到的BeanDefinitionHolder集合,将集合中的每一个mapper接口转换成MapperFactoryBean后,注册至spring容器
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions
// holder是一种封装参数的手法, 推荐使用
//处理扫描得到的BeanDefinitionHolder集合,将集合中的每一个mapper接口转换成MapperFactoryBean后,注册至spring容器
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
//增加一个构造方法,接口类型作为构造函数的入参
definition.getConstructorArgumentValues()
.addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//将bean的类型转换成mapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
//增加addToConfig属性
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
//增加sqlSessionFactory属性
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues()
.add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
//增加sqlSessionTemplate属性
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn(
"Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues()
.add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn(
"Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
//修改自动注入的方式 bytype
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug(
"Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
这个里面有个非常重要的步骤, 就是将扫描到的BeanDefinition转换成MapperFactoryBean, 这就和我们的手动配置MapperFactoryBean一样了