MyBatis 整合Spring扫描接口,以及Mapper接口创建代理对象的(mybatis-spring)源码分析

 

一、整合spring扫描(@MapperScan 具体实现细节)

  • 1》Spring Bean Factory 创建之后回调各个BeanFactoryPostProcessor
  • 2》ConfigurationClassPostProcessor processConfigBeanDefinitions 解析处理@Configuration注解标注的配置类
  • 3》 依次处理 application(@SpringBootConfiguration)、@ComponentScan()会处理所有注入的bean
  • 4》 处理每一个Configuration类,以及各种 ImportSelector 和ImportBeanDefinitionRegistrar
  • 6》@MapperScan 注解

          1 -》MapperScannerRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar)给容器注入

           2 -》MapperScannerConfigurer 并设置 basePackage, mapperFactoryBeanClass (默认org.mybatis.spring.mapper.MapperFactoryBean)等属性。MapperFactoryBean implements BeanFactoryPostProcessor(间接继承)、 InitializingBean等接口,创建完成对象后,spring会自动执行对用的后置处理器方法和初始化方法

           3 -》postProcessBeanDefinitionRegistry 创建 ClassPathMapperScanner 对象,设置 scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);等属性,最后调用 scanner.scan()

           4 -》先调用父类的doScan,在调用 私有方法 processBeanDefinitions(beanDefinitions);

调用父类的 super.doScan(basePackages) 扫面包下面的 mapper 接口并创建对应的 BeanDefinition,注意此时的 BeanDefinition 的 beanClass 是具体的mapper类

调用 私有方法 processBeanDefinitions(beanDefinitions); 重新设置 每一个beanDefinition 的 beanClass 为 org.mybatis.spring.mapper.MapperFactoryBean

 

至此每一个Mapper接口的工厂类设置为 org.mybatis.spring.mapper.MapperFactoryBean 并成功注入到spring 容器中

 

  • 7》org.mybatis.spring.mapper.MapperFactoryBean 解析初始化细节

MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> 类图如下

 

spring 初始化 InitializingBean 的实现类 后会回调 afterPropertiesSet 方法,进而调用 DaoSupport 的 checkDaoConfig 方法,MapperFactoryBean 重写了 父类的 checkDaoConfig 方法:

 

Configuration configuration = getSqlSession().getConfiguration(); // 通过SqlSession获取Configuration对象
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
  try {
    configuration.addMapper(this.mapperInterface); // 将mapper接口添加到mybatis configuration中,在addMapper方法中,mybatis会通过jdk动态代理产生代理对象
  } catch (Exception e) {
    logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
    throw new IllegalArgumentException(e);
  } finally {
    ErrorContext.instance().reset();
  }
}

FactoryBean 接口实现类,spring在getBean时会,调用对象的 getObject 返回实例。

在使用mapper进行依赖注入时,Spring会调用容器中对应 MapperFactoryBean 的 getObject() 方法,最中从mybatis中取出mapper接口的代理对象

@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

二、MyBatis创建Mapper接口代理对象流程

由第一步可以看出 org.apache.ibatis.session.Configuration 的 addMapper 方法进行代理对象的创建,先看 SqlSessionFactoryBean 如何创建 Configuration 对象的

  • 1》org.mybatis.spring.SqlSessionFactoryBean(需要使用者进行配置,springboot有默认的配置) 继承关系分析

 

      1.1、InitializingBean 接口 afterPropertiesSet 方法在 SqlSessionFactoryBean对象创建成功后被调用:

 

@Override
public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
      "Property 'configuration' and 'configLocation' can not specified with together");

  this.sqlSessionFactory = buildSqlSessionFactory();
}

buildSqlSessionFactory() 方法会通过,已经设置好的属性: dataSource、configLocation(classpath:mybatis-config.xml)、mapperLocations(classpath:com/bdqn/dao/**/*.xml)等创建 org.apache.ibatis.session.Configuration targetConfiguration,this.sqlSessionFactoryBuilder.build(targetConfiguration)构建出 SqlSessionFactory

    1.2、FactoryBean 接口实现类,spring在getBean时会,调用对象的 getObject 返回实例,即 SqlSessionFactory

 

  • 2》分析 org.apache.ibatis.session.Configuration 的 addMapper(this.mapperInterface)

     2.1 通过源码可以看出调用了成员变量 org.apache.ibatis.binding.MapperRegistry 的 .addMapper(type); 关联 上面第 7 步 代码如下:

public <T> void addMapper(Class<T> type) {
     //判断是否是接口
  if (type.isInterface()) {
     //判断是否已经注册过
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      //将mapper接口注册到map集合中
      knownMappers.put(type, new MapperProxyFactory<>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      // 解析接口上的注解或者加载mapper配置文件生成mappedStatement
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

 

 

    2.2 》MapperProxyFactory 分析

public class MapperProxyFactory<T> {
   // 被代理的接口
  private final Class<T> mapperInterface;
  // 方法进行缓存 
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
      //采用JDK的动态代理Proxy代理模式生成接口代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
     //MapperProxy 实现 InvocationHandler接口
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    //生程代理
    return newInstance(mapperProxy);
  }

}

    2.3》MapperProxy 分析:invoke 方法

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 (method.isDefault()) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  // MapperMethod 执行业务操作
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

MapperMethod 对象,处理SqlSession接口调用CRUD操作后产生的结果集。
 CURD execute方法

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);
        if (method.returnsOptional()
            && (result == null || !method.getReturnType().equals(result.getClass()))) {
          result = Optional.ofNullable(result);
        }
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值