说明
- 接下来几篇讲的是 mybatis-spring 的源码
- 目前分为4篇讲完,包含如下(@MapperScan ,sqlSessionFactory,mapperFactorBean,调用过程)
- 可能会穿插这spring-ioc的知识
- 首先你要知道springboot整合mybatis是怎么操作的,不会讲怎么使用
@MapperScan
直接看代码
//一个项目整合mybatis,最少你要配置两个东西
//1.写了sql的xml文件放在哪里的
mybatis.mapperLocations=classpath:mappers/*.xml
@SpringBootApplication
//2.dao接口在哪
@MapperScan(value="com.kiss.mxb.dao")
public class Test01Application {
public static void main(String[] args) {
SpringApplication.run(Test01Application.class, args);
}
}
//看下@mapperScan这个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//最最关键的就是这个@Import注解
//讲sprig-ioc 的时候说过,@Import处理三种类型的bean
//MapperScannerRegistrar 就是继承了其中一个,还是功能最强大的那个
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {}
//讲spring-ioc-@import 注解的时候说过,如果 import 的类是实现了ImportBeanDefinitionRegistrar,则执行它的registerBeanDefinitions方法
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
//就是这个方法,看些这个方法拿到了注解元数据和注册器
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取名字为MapperScan的注解的属性
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
........................................................
//获取 mapperScan 注解的 value属性,也就是我们配置的扫描的报名
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//也可以用basePackages属性配置扫描包名
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//basePackageClasses属性也可以
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
//来了老弟,去扫描
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//调用了spring的扫描的功能,以前讲spring-ioc的时候已经看过了,所以这里就不看了
//这里里面就是把你扫描到的bean注册到 beanDefinitionMap
//然后把这些 beanDefinition 返回
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 {
//这里是重点
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//循环这些beanDefinition
for (BeanDefinitionHolder holder : beanDefinitions) {
// 持有者转beanDefinition
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//下面的都是给这些beanDefinition设置属性
// 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
//这个地方是最骚的地方(重点)
//把当前的beanDefinition 的 beanClas换成 mapperFactoryBean,这是啥意思呢,意思就是 本来 有一台大众,但是现在壳子还是大众,但是里面的东西 都是换成了 法拉利
//mapperFactoryBean 实现了 FactoryBean
//getbean的时候获取的是 它 getObject的方法返回的bean
definition.setBeanClass(this.mapperFactoryBean.getClass());
//添加了 PropertyValues 下面的代码都不是很重要
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
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;
}
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;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
//这个地方 是最最最骚的 设置当前bean的依赖注入模型
//这个地方我科普下,我们一般使用的 @Autowired , @Resource 不清楚spring源码的都会说这两个注解 bytype byname,怎么说呢,说的对也不对.
//其实 spring 依赖注入的模型有四种 分别为 AUTOWIRE_NO ,AUTOWIRE_BY_NAME,AUTOWIRE_BY_TYPE,AUTOWIRE_CONSTRUCTOR
//我们正常的bean都是AUTOWIRE_NO 意外吧
//这个地方mybatis 把这些 mapperFactoryBean 的依赖注入模型都设置为 AUTOWIRE_BY_TYPE
//AUTOWIRE_BY_TYPE 的特征是,一个bean的依赖注入模型为AUTOWIRE_BY_TYPE时,只需要属性存在 set 方法,就会自动注入,不需要 @Autowired , @Resource
//在这里说这个只是让我们学到 MapperFactoryBean 的时候不会为一些事情纠结
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE
//);
}
}
}