注:为了便于理解源码逻辑,本文只截取了核心源码。
一、调用图解
二、源码解析
使用spring集成mybatis时只需要简单几步,此文不再赘述,下面我们从我们的配置类作为入口,一步步探索spring集成mybatis的过程:
- 首先是我们的配置类,我们的配置类中:
- 标注
@MapperScan
注解,值为扫描mapper包 - 一个
SqlSessionFactory
的@Bean方法
- 标注
@Configuration
@ComponentScan("com.lk.demo.mybatis_spring")
@MapperScan("com.lk.demo.mybatis_spring")
public class Config {
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
return factoryBean.getObject();
}
@Bean
DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://192.168.1.216:3306/lk_db");
dataSource.setUsername("root");
dataSource.setPassword("123");
return dataSource;
}
}
- 然后我们点进
MapperScan
注解。
看到了什么,老套路,通过import引入类,我们先别点进去,想想,这个MapperScannerRegistrar
里面做了什么呢?想不到是吧,没关系,带着这个问题,下面几步有答案。
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
- 继续,进入
MapperScannerRegistrar
。
它的父类是ImportBeanDefinitionRegistrar
,那我们就看registerBeanDefinitions
方法
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
哦~,MapperScannerRegistrar
里面创建了一个mapper扫描器(ClassPathMapperScanner)
,然后用它去扫描,扫描做了什么呢?
-
进入
ClassPathMapperScanner
ClassPathMapperScanner
有4个重要的方法:- registerFilters()
- doScan()
- processBeanDefinitions()
- isCandidateComponent()
1个重要的属性:
- mapperFactoryBean
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
// spring的scanner都是通过过滤器初步筛选类,所以这里加了一个IncludeFilter,它的作用是初筛剩下所有的类
public void registerFilters() {
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;//所有的类都合格
}
});
}
// spring的第二次筛选,此筛选后,只剩下了接口类
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
//只有接口,返回true
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
// 扫描mapper
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 通过父类的扫描完成初筛和二筛,此时只剩下了接口类的bd
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
// 你可以认为筛选出的这些bd是半成品,不成熟的,还需再加工一下
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
// 加工bd
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
// 遍历bd, 将bd的class设置成 MapperFactoryBean
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 此句的作用是spring在推算构造方法时,会使用带一个参数的构造器,且参数值是mapper接口类
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
// bd的beanClass是MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
// spring的di时,会扫描bd中beanClass的setter方法,然后注入
// 此设置最主要的目的是通过set方法,注入我们在配置类(即第1步)中的sqlSessionFactory。
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
- 最后了,我们来看
MapperFactoryBean
它是FactoryBean
,所以我们直接看它的getObject()
方法
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
// 哇哦~,它返回了mybatis的mapper,我们继续跟getSqlSession(),这个方法定义在父类SqlSessionDaoSupport中
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
}
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
// 当注入模型为 AUTOWIRE_BY_TYPE 时,会扫描bd中beanClass的setter方法,
// 并将我们在配置文件中的sqlSessionFactory注入
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
// this.sqlSession哪里来的呢,我们发现 setSqlSessionFactory()方法有
public SqlSession getSqlSession() {
return this.sqlSession;
}
}