(七)Mybatis持久化框架原理之@MapperScan注解方式和Spring集成(完结篇)

目录

一、Mybatis和Spring集成项目准备

1.DbConfig配置类

2.SystemCst常量类

二、UML类图和流程分析

1.UML类图分析

2.流程分析

三、源码分析

1.MapperScannerConfigurer类


一、Mybatis和Spring集成项目准备

学习本篇前可先看(六)Mybatis持久化框架原理之MapperScannerConfigurer方式和Spring集成,了解最基本的mybatis-spring集成方式后再来学习此方式效果更好。

第五篇中的applicationContext.xml文件、datasource.properties文件以及mybatis-config.xml这三个文件在此demo中可以删除,新增以下两个类:

1.DbConfig配置类

源码如下:

@Configuration
@MapperScans(
        @MapperScan(basePackages = SystemCst.BASE_PACKAGE, 
                sqlSessionTemplateRef = SystemCst.SQL_SESSION_TEMPLATE)
)
public class DbConfig {
    @Bean(SystemCst.DATASOURCE)
    public DataSource getDataSource() {
        OracleDataSource dataSource = null;
        try {
            dataSource = new OracleDataSource();
            dataSource.setDriverType(SystemCst.DRIVER);
            dataSource.setURL(SystemCst.URL);
            dataSource.setUser(SystemCst.USERNAME);
            dataSource.setPassword(SystemCst.PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }
    @Bean(SystemCst.SQL_SESSION_FACTORY)
    public SqlSessionFactory getSqlSessionFactoryBean(
            @Qualifier(SystemCst.DATASOURCE) DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = 
                new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        PathMatchingResourcePatternResolver resolver = 
                new PathMatchingResourcePatternResolver();
        try {
            sqlSessionFactoryBean.setMapperLocations(
                resolver.getResources(SystemCst.MAPPER_LOCATION));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            return sqlSessionFactoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

2.SystemCst常量类

源码如下,配置都是可替换的:

public interface SystemCst {
    String DATASOURCE = "datasource";
    String SQL_SESSION_FACTORY= "sqlSessionFactory";
    String SQL_SESSION_TEMPLATE = "sqlSessionTemplate";
    String DRIVER = "****";
    String URL = "****";
    String USERNAME = "****";
    String PASSWORD = "****";
    String MAPPER_LOCATION = "classpath*:*Mapper.xml";
    String BASE_PACKAGE = "com.**.mapper";
}

按上述配置完就可以直接运行了,当然还有其他的配置方式,但只要搞懂这些方式的原理,就可以理解任何一种配置方式。

二、UML类图和流程分析

1.UML类图分析

该配置方式涉及的类图:

该图相比于第六篇而言,大致没什么变化,并且把上一篇已经介绍过的接口剔除了此种配置相比而言多了MapperScannerRegistrar以及它实现的接口,详细介绍如下:

  • ResourceLoaderAware接口:该接口将会在ResourceLoader(ApplicationCOntext一般而言会实现该接口)运行时调用接口方法setResourceLoader,也可以使用spring的接口ApplicationContextAware来代替,目前mybatis-spring对于该接口的实现为空;
  • ImportBeanDefinitionRegistrar接口:处理@Configuration注解类时注册额外的bean定义;
  • MapperScannerRegistrar类:对mybatis注解@MapperScan和@MapperScans进行处理的类,通过读取注解中的值在方法中修改MapperScannerConfigurer bean定义,从而实现用MapperScannerConfigurer类来管理mybatis和spring的集成。

2.流程分析

其流程图如下:

可以看到其相较于上一篇介绍的集成方式而言,中间只是多了@MapperScan注解引入MapperScannerRegistrar类,再通过这个类注册MapperScannerConfigurer类及配置注解中相应的属性即可完成集成。当Spring工厂有MapperScannerConfigurer这个类的bean定义后,在处理BeanFactoryPostProcessor接口时将会获取MapperScannerConfigurer类,进而完成第六章所述流程。

三、源码分析

1.MapperScannerConfigurer类

该类实现ImportBeanDefinitionRegistrar接口允许注解配置mybatis对mapper进行扫描,扫描到basePackage之后再根据MapperScannerConfigurer类来实现mybatis和spring的集成。源码如下:

public class MapperScannerRegistrar 
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  @Override
  public void registerBeanDefinitions(
          AnnotationMetadata importingClassMetadata, 
          BeanDefinitionRegistry registry) {
    // ImportBeanDefinitionRegistrar接口的方法,解析完@Import注解后将被调用
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata
                .getAnnotationAttributes(MapperScan.class.getName()));
    // 如果@MapperScan注解的属性不为空则调用方法registerBeanDefinitions
    // 去注册MapperScannerConfigurer类到Spring工厂中
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry, 
              generateBaseBeanName(importingClassMetadata, 0));
    }
  }
  void registerBeanDefinitions(AnnotationAttributes annoAttrs, 
          BeanDefinitionRegistry registry, String beanName) {
    // 为MapperScannerConfigurer类创建一个Bean定义构造器
    BeanDefinitionBuilder builder = 
            BeanDefinitionBuilder
                .genericBeanDefinition(MapperScannerConfigurer.class);
    // processPropertyPlaceHolders这里会默认设置为true,如果为true则会先从
    // PropertyValues和environment中查找对应的basePackage和lazyInitialization
    // 等属性,如果这些属性MapperScannerConfigurer类中配置了则去配置的属性
    builder.addPropertyValue("processPropertyPlaceHolders", true);
    // 下面则是从注解的属性中获得相应的属性,再放到builder的PropertyValues中
    Class<? extends Annotation> annotationClass = 
            annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }
    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }
    Class<? extends BeanNameGenerator> generatorClass = 
            annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      builder.addPropertyValue("nameGenerator", 
              BeanUtils.instantiateClass(generatorClass));
    }
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = 
            annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      builder.addPropertyValue("mapperFactoryBeanClass", 
              mapperFactoryBeanClass);
    }
    String sqlSessionTemplateRef = 
            annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
      builder.addPropertyValue("sqlSessionTemplateBeanName", 
              annoAttrs.getString("sqlSessionTemplateRef"));
    }
    String sqlSessionFactoryRef = 
            annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
      builder.addPropertyValue("sqlSessionFactoryBeanName", 
              annoAttrs.getString("sqlSessionFactoryRef"));
    }
    // 解析@MapperScan的value、basePackages和basePackageClasses属性
    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value"))
        .filter(StringUtils::hasText).collect(Collectors.toList()));
    basePackages.addAll(Arrays
        .stream(annoAttrs.getStringArray("basePackages"))
        .filter(StringUtils::hasText)
        .collect(Collectors.toList()));
    basePackages.addAll(Arrays.stream(annoAttrs
        .getClassArray("basePackageClasses"))
        .map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));
    // 懒加载
    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }
    // 将前面获得的包路径设置成XX,XX,XX格式
    builder.addPropertyValue("basePackage", 
            StringUtils.collectionToCommaDelimitedString(basePackages));
    // 将bean定义注册到注册中心
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
  }
  // 处理@MapperScans注解,里面有多个@MapperScan注解
  static class RepeatingRegistrar extends MapperScannerRegistrar {
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, 
            BeanDefinitionRegistry registry) {
      AnnotationAttributes mapperScansAttrs = AnnotationAttributes
          .fromMap(importingClassMetadata
          .getAnnotationAttributes(MapperScans.class.getName()));
      // 如果@MapperScans里面的@MapperScan不为空
      if (mapperScansAttrs != null) {
        AnnotationAttributes[] annotations = 
                mapperScansAttrs.getAnnotationArray("value");
        // 依次传入@MapperScan调用registerBeanDefinitions进行解析注册
        for (int i = 0; i < annotations.length; i++) {
          registerBeanDefinitions(annotations[i], registry, 
                  generateBaseBeanName(importingClassMetadata, i));
        }
      }
    }
  }
}

该类方法流程分析:

  1. 如果使用的注解是@MapperScan则调用MapperScannerRegistrar,注解是@MapperScans则调用MapperScannerRegistrar类中的RepeatingRegistrar类;
  2. 在被@Configuration注解的类进行处理时,会调用ImportBeanDefinitionRegistrar接口的方法,该方法会将注解中的值使用AnnotationAttributes.fromMap()方法转变成AnnotationAttributes对象,再传入本类中的registerBeanDefinitions方法;
  3. 在registerBeanDefinitions方法中,会创建MapperScannerConfigurer类的bean定义构造类,然后将注解中的markerInterface、mapperFactoryBeanClass、sqlSessionFactoryBeanName等属性一次添加进BeanDefinitionBuilder类中,并在最后将MapperScannerConfigurer的bean定义注册进BeanDefinitionRegistry,此过程相当于完成第四篇中applicationContext中对MapperScannerConfigurer类的配置,并将类配置以bean定义的形式注册进BeanDefinitionRegistry中;
  4. 后续的步骤和第六篇的MapperScannerConfigurer方式集成spring一样,以该类的回调函数为入口,扫描mapper接口,并配置其他的属性等。

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值