zoukankan      html  css  js  c++  java
  • mybatis源码分析

    说明:以下分析基于spring-framework-5.0.x,mybatis-spring-1.3.2,mybatis-3.4.6相关源码可自行去github下载或者maven依赖然后利用类似ideal工具自动关联源码功能。

    我们知道spring对bean的管理,我们可以通过多种方式将bean添加进spring中进行管理其中包括:

    • 一般注解@Conponent、@Controller、@Service
    • @Import形式有三种:第一种用法:@Import({ 要导入的容器中的组件 } ):容器会自动注册这个组件,id默认是全类,第二种用法:ImportSelector:返回需要导入的组件的全类名数组,springboot底层用的特别多第三种用法:ImportBeanDefinitionRegistrar(如Mybatis和springboot整合时):手动注册bean到容器。
    • 当然也可以通过BeanDefinitionRegistryPostProcessor(BeanFactoryPostProcessor子类)进行bean注册。通过它的重写方法参数可以拿到register进而进行bean的注册或者
    • 使用beanRegister或者bean工厂

    一:bean交由spring管理
    mybatis通过@MapperScan标签完成对接口mapper的注入,我们依次点击@MapperScan->@Import(MapperScannerRegistrar.class)->MapperScannerRegistrar implements ImportBeanDefinitionRegistrar通过重写registerBeanDefinitions这个方法完成bean的注入。部分相关代码如下:

    @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ...for (String pkg : annoAttrs.getStringArray("basePackages")) {
          if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
         ...scanner.doScan(StringUtils.toStringArray(basePackages));
    }
    @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
          logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
          processBeanDefinitions(beanDefinitions);
        }
        return beanDefinitions;
      }
     private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        for (BeanDefinitionHolder holder : beanDefinitions) {
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
        ...
          // the mapper interface is the original class of the bean but, the actual class of the bean is MapperFactoryBean
          definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
          definition.setBeanClass(this.mapperFactoryBean.getClass());
    }
    

    二:MapperFactoryBean:MapperFactoryBean->SqlSessionDaoSupport->DaoSupport->InitializingBean(在bean实例化前执行checkDaoConfig方法)

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }//把接口类型传入factoryBean便于代理对象的产生
    @Override
      protected void checkDaoConfig() {
        ...
            configuration.addMapper(this.mapperInterface)->mapperRegistry.addMapper(type)->parser.parse()(完成一些注解解析及sql生成等)
        ...
      }
    }
    @Override
      public T getObject() throws Exception {
        //getSqlSession()->SqlSessionTemplate 在MapperFactoryBean实例化过程中,会首先根据mybatis-spring-boot-autoconfigure中META-INF/spring.factories中的内容获取到MybatisAutoConfiguration这个配置类类名。在后续的bean创建过        程中,MybatisAutoConfiguration类的对象会被创建成一个继承了FactoryBean的代理对象放到bean工厂中 MybatisAutoConfiguration中SqlSessionTemplate sqlSessionFactory两个类的的生成是@Bean注解来生成
        return getSqlSession().getMapper(this.mapperInterface);->>>
           public T newInstance(SqlSession sqlSession) {
           final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
           return newInstance(mapperProxy);
          } 
      }
      }
    
    public class MapperProxy<T> implements InvocationHandler, Serializable {
    @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       ...
            return method.invoke(this, args);
         ...
        return mapperMethod.execute(sqlSession, args);
      }
    }
    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 SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {
              executeWithResultHandler(sqlSession, args);
              result = null;
            } else if (method.returnsMany()) {
              result = executeForMany(sqlSession, args);
           ...
      }
    private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        ...
          result = sqlSession.<E>selectList(command.getName(), param);
       ...
      }
    //sqlSession为SqlSessionTemplate
    @Override
      public <E> List<E> selectList(String statement, Object parameter) {
        return this.sqlSessionProxy.<E> selectList(statement, parameter);//执行invoke方法
      }
    private class SqlSessionInterceptor implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          SqlSession sqlSession = getSqlSession(
           ...
          } finally {
            if (sqlSession != null) {
              closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);//mybatis结合spring时一级缓存失效原因
            }
          }
        }
      }
    
  • 相关阅读:
    容器平台选型的十大模式:Docker、DC/OS、K8S 谁与当先?
    Spring Controller里注入Feign的Interface报红提示的问题
    几种常见的日志
    个人博客搭建方案选择
    elasticsearch常用操作命令
    kafka操作命令
    centos7.0安装java环境
    CentOS安装jdk的三种方法
    在OAuth2中 自定义tokenServices来提供个性化服务,每次刷新token并让原token在5分钟内有效
    解决HttpServletRequest的输入流只能读取一次的问题(转)
  • 原文地址:https://www.cnblogs.com/leifonlyone/p/12698615.html
Copyright © 2011-2022 走看看