mybatis与spring的整合之ClassPathMapperScanner

mybatis与spring的整合之ClassPathMapperScanner

  • java配置应用
    • 配置类
  • 源码分析
    • @MapperScan
    • MapperScannerRegistrar.registerBeanDefinitions
    • ClassPathMapperScanner.doScan
      • 父类ClassPathBeanDefinitionScanner.doScan扫描注册bd
      • processBeanDefinitions处理注册后的bd
        • 1.设置bd的构造函数参数值为当前bd的类名,也就是映射接口的类名
        • 2.改变bd的`beanClass`为**MapperFactoryBean**.`class`
        • 3.添加属性`addToConfig`为`true`
        • 4.设置属性按类型自动注入
    • 生成代理对象

缘起:之前写了一篇xml的配置源码分析mybatis与spring的整合之MapperFactoryBean,本篇讲解注解式自动扫描的源码分析

java配置应用

启用注解@MapperScan

配置类

只需要这么一个配置类,就完成了MyBatis-Spring的整合,具体看@MapperScan注解

@Configuration
@ComponentScan("cn.yukang.javaConfig")// 扫描包
@PropertySource("classpath:datasource.properties")// 加载数据源配置信息
@MapperScan("cn.yukang.javaConfig.mapper")
public class MyConfig {

	@Value("${jdbc.driverClassName}")
	private String driverclassname;
	
	@Value("${jdbc.url}")
	private String url;
	
	@Value("${jdbc.username}")
	private String userName;
	
	@Value("${jdbc.password}")
	private String passWord; 
	
	@Bean
	public BasicDataSource dataSource(){
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName(driverclassname);
		dataSource.setUrl(url);
		dataSource.setUsername(userName);
		dataSource.setPassword(passWord);
		return dataSource;
	}
	
	@Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 数据库与javaBean映射关系
        //sessionFactory.setTypeAliasesPackage("cn.yukang.javaConfig.bean");
        // 添加映射-使用接口+xml的形式(xml的命名空间为接口)
        //sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper.xml"));
        return sessionFactory;
    }
}

源码分析

@MapperScan

通过@Import导入了MapperScannerRegistrar

@Import({MapperScannerRegistrar.class})
public @interface MapperScan

MapperScannerRegistrar.registerBeanDefinitions

mybatis与spring的整合之ClassPathMapperScanner_第1张图片
MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法,可以详看另一篇文章处理
带有ImportBeanDefinitionRegistrar的类

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    if (this.resourceLoader != null) {
        scanner.setResourceLoader(this.resourceLoader);
    }
	// 处理@MapperScan注解中的属性
    Class annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
        scanner.setAnnotationClass(annotationClass);
    }

    Class markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
        scanner.setMarkerInterface(markerInterface);
    }

    Class generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
        scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
    }

    Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
        scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    List basePackages = new ArrayList();
    String[] var10 = annoAttrs.getStringArray("value");
    int var11 = var10.length;

    int var12;
    String pkg;
    for(var12 = 0; var12 < var11; ++var12) {
        pkg = var10[var12];
        if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
        }
    }

    var10 = annoAttrs.getStringArray("basePackages");
    var11 = var10.length;

    for(var12 = 0; var12 < var11; ++var12) {
        pkg = var10[var12];
        if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
        }
    }

    Class[] var14 = annoAttrs.getClassArray("basePackageClasses");
    var11 = var14.length;

    for(var12 = 0; var12 < var11; ++var12) {
        Class clazz = var14[var12];
        basePackages.add(ClassUtils.getPackageName(clazz));
    }

    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
}

上面方法内容就是获取@MapperScan注解中的属性值, 比如@MapperScan("cn.yukang.javaConfig.mapper")就是获取value属性,放入集合basePackages中,然后调用scanner.doScan(StringUtils.toStringArray(basePackages));开始扫描注册

ClassPathMapperScanner.doScan

public Set doScan(String... basePackages) {
    // 调用父类ClassPathBeanDefinitionScanner.doScan
    Set beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
        this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
        // 处理注册后的bd
        this.processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

父类ClassPathBeanDefinitionScanner.doScan扫描注册bd

这里先调用父类ClassPathBeanDefinitionScanner.doScan方法扫描包路径并注册bd。可详看另一篇文章spring加载流程之ClassPathBeanDefinitionScanner

processBeanDefinitions处理注册后的bd

循环处理bd

1.设置bd的构造函数参数值为当前bd的类名,也就是映射接口的类名

definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());

2.改变bd的beanClassMapperFactoryBean.class

definition.setBeanClass(this.mapperFactoryBean.getClass());

3.添加属性addToConfigtrue

definition.getPropertyValues().add("addToConfig", this.addToConfig);

4.设置属性按类型自动注入

如果没有显示的指定SqlSessionFactoryBean,则设置当前bd的属性注入方式为按类型注入,与在属性上加了@Autowired效果一致

if (!explicitFactoryUsed) {
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
    }
    // 设置属性按类型注入
    definition.setAutowireMode(2);
}

因为没有手动注入SqlSessionFactoryBeanMapperFactoryBean,所以在spring实例化bd注入属性的时候,会按类型注入的方式找到类型为SqlSessionFactoryBean的进行注入

生成代理对象

生成代理对象就由MapperFactoryBean去完成了mybatis与spring的整合之MapperFactoryBean

你可能感兴趣的:(mybatis源码)