mybatis-spring @MapperScan走读分析

接上一篇文章:https://blog.csdn.net/qq_26437925/article/details/145100531, 本文注解分析mybatis-spring中的@MapperScan注解,则将容易许多。

目录

@MapperScan注解定义

@MapperScan注解加入了@Import(MapperScannerRegistrar.class), 所以可以知晓

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

  /**
   * Alias for the {@link #basePackages()} attribute. Allows for more concise
   * annotation declarations e.g.:
   * {@code @EnableMyBatisMapperScanner("org.my.pkg")} instead of {@code
   * @EnableMyBatisMapperScanner(basePackages= "org.my.pkg"})}.
   */
  String[] value() default {};

  /**
   * Base packages to scan for MyBatis interfaces. Note that only interfaces
   * with at least one method will be registered; concrete classes will be
   * ignored.
   */
  String[] basePackages() default {};

  /**
   * Type-safe alternative to {@link #basePackages()} for specifying the packages
   * to scan for annotated components. The package of each class specified will be scanned.
   * <p>Consider creating a special no-op marker class or interface in each package
   * that serves no purpose other than being referenced by this attribute.
   */
  Class<?>[] basePackageClasses() default {};

  /**
   * The {@link BeanNameGenerator} class to be used for naming detected components
   * within the Spring container.
   */
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  /**
   * This property specifies the annotation that the scanner will search for.
   * <p>
   * The scanner will register all interfaces in the base package that also have
   * the specified annotation.
   * <p>
   * Note this can be combined with markerInterface.
   */
  Class<? extends Annotation> annotationClass() default Annotation.class;

  /**
   * This property specifies the parent that the scanner will search for.
   * <p>
   * The scanner will register all interfaces in the base package that also have
   * the specified interface class as a parent.
   * <p>
   * Note this can be combined with annotationClass.
   */
  Class<?> markerInterface() default Class.class;

  /**
   * Specifies which {@code SqlSessionTemplate} to use in the case that there is
   * more than one in the spring context. Usually this is only needed when you
   * have more than one datasource.
   */
  String sqlSessionTemplateRef() default "";

  /**
   * Specifies which {@code SqlSessionFactory} to use in the case that there is
   * more than one in the spring context. Usually this is only needed when you
   * have more than one datasource.
   */
  String sqlSessionFactoryRef() default "";

  /**
   * Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean.
   *
   */
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

}

ConfigurationClassPostProcessor扫描注册beanDefinition

mybatis-spring中自定义了org.mybatis.spring.annotation.MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,所以能被处理

mybatis-spring也实现了自己的Scanner能够扫描出所有的@Mapper的interface, 然后定义出BeanDefinition

继续debug可以看到对BeanDefinisiton 会设置BeanClass为FactoryBean类

即自定义的org.mybatis.spring.mapper.MapperFactoryBean

org.mybatis.spring.mapper.MapperFactoryBean

仍然是重点关注getObject方法

java 复制代码
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

数据库操作也封装为SqlSession接口,有多个实现类可选择

最后也会发现是java 动态代理

org.apache.ibatis.binding.MapperProxy

代理的具体实现则在mybatis依赖的MapperProxy类中

java 复制代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

sql执行

java 复制代码
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
    Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName() 
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

org.apache.ibatis.executor.SimpleExecutor#doUpdate

继续深入找一个简单的sql执行实现类,可以看到类似jdbc的处理流程

总结

最后还是回到了上一篇博文:https://blog.csdn.net/qq_26437925/article/details/145100531。只不过mybatis-spring要丰富一些,原理仍然是动态代理BeanFactoryPostProcessorFactoryBeanBeanPostProcessor这些基础知识。

相关推荐
我是Superman丶18 分钟前
在 PostgreSQL 中使用 JSONB 类型并结合 MyBatis-Plus 实现自动注入,主要有以下几种方案
数据库·postgresql·mybatis
爬山算法25 分钟前
Hibernate(85)如何在持续集成/持续部署(CI/CD)中使用Hibernate?
java·ci/cd·hibernate
Pluto_CSND1 小时前
基于mybatis-generator插件生成指定数据表的实体类、xml文件和dao层接口
mybatis
菜鸟233号1 小时前
力扣647 回文子串 java实现
java·数据结构·leetcode·动态规划
qq_12498707531 小时前
基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)
java·开发语言·前端·spring boot·spring·毕业设计·计算机毕业设计
h7ml1 小时前
查券返利机器人的OCR识别集成:Java Tesseract+OpenCV优化图片验证码的自动解析方案
java·机器人·ocr
野犬寒鸦1 小时前
从零起步学习并发编程 || 第五章:悲观锁与乐观锁的思想与实现及实战应用与问题
java·服务器·数据库·学习·语言模型
Volunteer Technology1 小时前
Sentinel的限流算法
java·python·算法
岁岁种桃花儿1 小时前
SpringCloud从入门到上天:Nacos做微服务注册中心
java·spring cloud·微服务
jdyzzy1 小时前
什么是 JIT 精益生产模式?它与传统的生产管控方式有何不同?
java·大数据·人工智能·jit