栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

mybatis源码解析三-Mapper接口调用解读

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

mybatis源码解析三-Mapper接口调用解读

Mapper.class接口调用解读
public static void select() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory =
            new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 根据类型得到接口,接口直接调用
    TransactionMapper transactionMapper = sqlSession.getMapper(TransactionMapper.class);
    // 代理类调用,得到返回值与sqlSession对比
    // String flowKey = sqlSession.selectOne("com.dzq.mapper.TransactionMapper.getFlowKey", 10);
    // SqlSession调用知道namespace和参数,那么接口调用如何知道呢,通过动态代理的object和method方法拼接,在根据方法对应的标签找到是insert还是update
    // 然后再得到参数
    String flowKey = transactionMapper.getFlowKey(10);
    System.out.println(flowKey);
}

上面已经讲到InvoceHandler接口的方法,那什么时候加入到配置中的呢

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // 解析mapper文件
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    // 找到对应的namespace的接口进行解析
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}
private void bindMapperForNamespace() {
  // 得到namespace
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class boundType = null;
    try {
      // 转换到class类
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      if (!configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:" + namespace);
        // 加入到config
        configuration.addMapper(boundType);
      }
    }
  }
}
public  void addMapper(Class type) {
  // 校验工作
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      // 放入到mappers,new MapperProxyFactory(type)创建动态代理工厂,如上面复习的一样
      knownMappers.put(type, new MapperProxyFactory(type));
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

下面讲如何获取

TransactionMapper transactionMapper = sqlSession.getMapper(TransactionMapper.class);
public  T getMapper(Class type, SqlSession sqlSession) {
  // 得到对应的动态代理工厂
  final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    // 创建代理,如最开始的复习,执行主要看MapperProxy的invoke方法
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // 调用了Object方法和接口的默认方法,不是重点
  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);
  }
  // 根据方法生成MapperMethod方法,后面比较简单,就是创建MapperMethod
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());

MapperMethod关键属性和方法

// command:sql命令,记录着命令类型:insert,update等,还有namespace
// method:记录返回值,参数等
private final SqlCommand command;
private final MethodSignature method;
// 执行,在创建之后就执行了
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  // 根据不同的类型调用不同的sqlSession方法
  switch (command.getType()) {
    case INSERT: {
    //根据method组装参数,因为sqlSession只有一个参数
    //command.getName()=namespace
    Object param = method.convertArgsToSqlCommandParam(args);
      // rowCountResult 根据sqlSession执行的结果转换返回值,返回值类型在method记录
      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;
}
与Spring结合

为了能够结合Spring,mybatis特意扩展开发mybatis-spring.

mybatis能够实现SqlSession获取到Mapper类,那么就看看如何注入到springbean中。

spring复习
  1. spring bean存在的2种形式:普通的bean和工厂bean

普通的bean:直接获取对应类的示例,比如以下,dataSource直接获取DriverManagerDataSource属性


    
    
    
    

工厂bean:获取对应bean的工厂,通过getObject()获取,比如以下,sqlSessionFactory获取SqlSessionFactoryBean通过getObject()获得。


    
    

为什么需要FactoryBean,有一些配置不仅仅是一个属性那么简单,需要大量的解析操作(代码量),通过上面的源码知道SqlSessionFactory是持有Config的,config是通过解析文件来获得的。那么什么时候解析呢?通常和InitializingBean配合,实现afterPropertiesSet,当属性赋值完成后,可以根据属性值完成解析工作。解析完成后创建SqlSessionFactory,然后通过FactoryBean接口就可以获取了。

// 属性赋值完成后,通过属性进行下一步操作
@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");
  // 构建sqlSessionFactory,buildSqlSessionFactory不在介绍
  this.sqlSessionFactory = buildSqlSessionFactory();
}
// 通过上面的构建,这里就直接获取了
@Override
public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }

  return this.sqlSessionFactory;
}
  1. spring对BeanDefinition处理扩展,继承BeanDefinitionRegistryPostProcessor,实现postProcessBeanDefinitionRegistry

BeanDefinition是对bean注解的解析,还没有生成bean对象,可以通过postProcessBeanDefinitionRegistry进行处理,获取进行其他处理逻辑,相当于spring的一个监听器,做一些额外的事情。比如:org.mybatis.spring.mapper.MapperScannerConfigurer,其实并不是为了使用这个类对象,只是为了把对应的Mapper接口注册到spring。任何类都可以,只要符合业务逻辑的类名即可。

回忆下mybatis,Mapper代理是可以通过SqlSeesion对象通过类获取到的,那么把这个对象注入到bean就可以了,bean的2种形式已经介绍过了,mybatis是通过工厂bean的方式注入的,MapperFactoryBean,那看看源码是如何通过postProcessBeanDefinitionRegistry注入的吧?

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }
  // 通过ClassPathMapperScanner扫描包
  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  // 赋值MapperFactoryBeanClass类
  //public void setMapperFactoryBeanClass(Class mapperFactoryBeanClass) {
  //  赋值的时候为空,默认值MapperFactoryBean
  //  this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
  //}
  scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  if (StringUtils.hasText(lazyInitialization)) {
    scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  }
  if (StringUtils.hasText(defaultScope)) {
    scanner.setDefaultScope(defaultScope);
  }
  scanner.registerFilters();
  // 开始解析,那么就看看生成什么样子的BeanDefinition
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

开始解析

// 开始解析
public int scan(String... basePackages) {
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
   // ClassPathMapperScanner重新了这个方法,一定要看重写的方法
   doScan(basePackages);

   // Register annotation config processors, if necessary.
   if (this.includeAnnotationConfig) {
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
   }

   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

子类重写的方法

@Override
public Set doScan(String... basePackages) {
  // 还是调用父类获取到解析的beanDefinitions
  Set beanDefinitions = super.doScan(basePackages);

  if (beanDefinitions.isEmpty()) {
    LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
        + "' package. Please check your configuration.");
  } else {
    // 开始操作BeanDefinition
    processBeanDefinitions(beanDefinitions);
  }

  return beanDefinitions;
}

对BeanDefinition进行二次加工

private void processBeanDefinitions(Set beanDefinitions) {
  AbstractBeanDefinition definition;
  BeanDefinitionRegistry registry = getRegistry();
  for (BeanDefinitionHolder holder : beanDefinitions) {
    definition = (AbstractBeanDefinition) holder.getBeanDefinition();
    boolean scopedProxy = false;
    if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
      definition = (AbstractBeanDefinition) Optional
          .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
          .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
              "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
      scopedProxy = true;
    }
    String beanClassName = definition.getBeanClassName();
    LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
        + "' mapperInterface");

    // MapperFactoryBean构造函数唯一的参数就是Mapper本身
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
    // BeanClass替换成mapperFactoryBeanClass:MapperFactoryBean
    definition.setBeanClass(this.mapperFactoryBeanClass);

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

    // Attribute for MockitoPostProcessor
    // https://github.com/mybatis/spring-boot-starter/issues/475
    definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName);

    boolean explicitFactoryUsed = false;
    if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
      definition.getPropertyValues().add("sqlSessionFactory",
          new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
      explicitFactoryUsed = true;
    } else if (this.sqlSessionFactory != null) {
      definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
      explicitFactoryUsed = true;
    }

    if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
      if (explicitFactoryUsed) {
        LOGGER.warn(
            () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
      }
      definition.getPropertyValues().add("sqlSessionTemplate",
          new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
      explicitFactoryUsed = true;
    } else if (this.sqlSessionTemplate != null) {
      if (explicitFactoryUsed) {
        LOGGER.warn(
            () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
      }
      definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
      explicitFactoryUsed = true;
    }
	// 上面的代码把属性值放入到bean定义中,通过byType方式赋值属性
    // public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    //   if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
    //     this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    //   }
    // }
    if (!explicitFactoryUsed) {
      LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }

    definition.setLazyInit(lazyInitialization);

    if (scopedProxy) {
      continue;
    }

    if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
      definition.setScope(defaultScope);
    }

    if (!definition.isSingleton()) {
      BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
      if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
        registry.removeBeanDefinition(proxyHolder.getBeanName());
      }
      registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
    }

  }
}

至此Mybatis如何与spring结合讲解完成。

设计模式总结 工厂模式
  1. SqlSessionFactory:负责创建SqlSession
public interface SqlSessionFactory {
  // 创建openSession无参,关键参数默认
  SqlSession openSession();
  // 创建是否自动提交的SqlSession,参数属于Connection的重要属性,以下方法类似
  // 关键属性由创建者决定
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  Configuration getConfiguration();
}
构造器模式
  1. SqlSessionFactoryBuilder负责构建SqlSessionFactory

使用心得:SqlSessionFactory实现类的构造方法public DefaultSqlSessionFactory(Configuration configuration),目前已有的资源(xml文件)需要转换,那么直接使用构造器,拿到xml,然后再build中进行转换来构造SqlSessionFactory

// 通过额外的参数转换成SqlSessionFactory真正需要的参数
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {  
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    // 最终是拿到Config创建SqlSessionFactory,使得SqlSessionFactory代码内的构造器更加简洁
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      reader.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/606004.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号