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这些基础知识。

相关推荐
ZZHow10241 小时前
JavaWeb开发_Day05
java·笔记·web
CHEN5_021 小时前
【Java虚拟机】垃圾回收机制
java·开发语言·jvm
Warren981 小时前
Lua 脚本在 Redis 中的应用
java·前端·网络·vue.js·redis·junit·lua
艾伦~耶格尔5 小时前
【数据结构进阶】
java·开发语言·数据结构·学习·面试
爪洼传承人5 小时前
18- 网络编程
java·网络编程
smileNicky6 小时前
SpringBoot系列之从繁琐配置到一键启动之旅
java·spring boot·后端
祈祷苍天赐我java之术6 小时前
Java 迭代器(Iterator)详解
java·开发语言
David爱编程6 小时前
为什么必须学并发编程?一文带你看懂从单线程到多线程的演进史
java·后端
我命由我123456 小时前
软件开发 - 避免过多的 if-else 语句(使用策略模式、使用映射表、使用枚举、使用函数式编程)
java·开发语言·javascript·设计模式·java-ee·策略模式·js
long3167 小时前
java 策略模式 demo
java·开发语言·后端·spring·设计模式