MyBatis-plus源碼解析


MyBatis-plus是完全基於MyBatis開發的一個增強工具,是在MyBatis的基礎上做增強的框架,為簡化開發、提高效率而生。它在MyBatis原本的框架上增加了很多實用性功能,比如樂觀鎖插件、字段自動填充功能、分頁插件、條件構造器、sql 注入器等等。使用 MyBatis-plus 可以完全不寫任何 XML 文件,直接使用繼承了BaseMapper 接口的類對象完成對數據庫的映射操作
基於映射的原理,MyBatis-plus 必然要實現 Mapper中的方法與 SQL 語句的對應轉化,以下即為 MyBatis-plus 重要流程圖例。

1. Mapper 對象方法映射為 SQL 語句
1)在 MyBatis-plus 中, MybatisPlusAutoConfiguration 自動配置類的 sqlSessionFactory()方法為 Spring提供創建 sqlSession的工廠類對象,對 sqlSessionFactory 進行定義的定義類變為了 MybatisSqlSessionFactoryBean。在 sqlSessionFactory()方法中,除了注入 MyBatis本身的組件,還會注入MyBatis-plus 的 主鍵生成器、SQL 注入器等組件,最后通過 MybatisSqlSessionFactoryBean#getObject() 方法獲取到 sqlSessionFactory 對象

 @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        applyConfiguration(factory);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (this.properties.getTypeAliasesSuperType() != null) {
            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            factory.setTypeHandlers(this.typeHandlers);
        }
        Resource[] mapperLocations = this.properties.resolveMapperLocations();
        if (!ObjectUtils.isEmpty(mapperLocations)) {
            factory.setMapperLocations(mapperLocations);
        }
        // TODO 修改源碼支持定義 TransactionFactory
        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);

        // TODO 對源碼做了一定的修改(因為源碼適配了老舊的mybatis版本,但我們不需要適配)
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (!ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
        }
        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);

        // TODO 自定義枚舉包
        if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
            factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
        }
        // TODO 此處必為非 NULL
        GlobalConfig globalConfig = this.properties.getGlobalConfig();
        // TODO 注入填充器
        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
        // TODO 注入主鍵生成器
        this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
        // TODO 注入sql注入器
        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
        // TODO 注入ID生成器
        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
        // TODO 設置 GlobalConfig 到 MybatisSqlSessionFactoryBean
        factory.setGlobalConfig(globalConfig);
        return factory.getObject();
    }

2)、MybatisSqlSessionFactoryBean#getObject() 執行懶加載策略,最后通過 buildSqlSessionFactory() 方法創建 SqlSessionFactory 工廠類對象。這個方法的流程很長,不過大致可以分為兩個步驟:

  • 1.創建 MybatisXMLConfigBuilder 對象,調用其 parse() 方法去解析 XML 配置文件及 Mapper
  • 2.解析獲得的信息存儲在 targetConfiguration 對象中,根據其信息創建 SqlSessionFactory 對象

3)、MybatisXMLConfigBuilder#parse() 會去解析配置文件,最后會調用到其內部方法 mapperElement()。這個方法完成解析 Mapper工作,並將其添加到配置類 MybatisConfiguration 中

    private void parseConfiguration(XNode root) {
        try {
            // issue #117 read properties first
            propertiesElement(root.evalNode("properties"));
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfs(settings);
            loadCustomLogImpl(settings);
            typeAliasesElement(root.evalNode("typeAliases"));
            pluginElement(root.evalNode("plugins"));
            objectFactoryElement(root.evalNode("objectFactory"));
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            typeHandlerElement(root.evalNode("typeHandlers"));
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
  private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                if ("package".equals(child.getName())) {
                    String mapperPackage = child.getStringAttribute("name");
                    configuration.addMappers(mapperPackage);
                } else {
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    if (resource != null && url == null && mapperClass == null) {
                        ErrorContext.instance().resource(resource);
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) {
                        ErrorContext.instance().resource(url);
                        InputStream inputStream = Resources.getUrlAsStream(url);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url == null && mapperClass != null) {
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        configuration.addMapper(mapperInterface);
                    } else {
                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                }
            }
        }
    }

4)、MybatisConfiguration#addMapper()方法其實是去調用 MybatisMapperRegistry#addMapper() 方法,其核心是MybatisMapperAnnotationBuilder#parse()

    @Override
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                // TODO 如果之前注入 直接返回
                return;
                // TODO 這里就不拋異常了
//                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // TODO 這里也換成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
                knownMappers.put(type, new MybatisMapperProxyFactory<>(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.
                // TODO 這里也換成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
                MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }

5)、MybatisMapperAnnotationBuilder#parse() 方法真正開始完成 Mapper 接口中的方法與 SQL 語句的映射,其中 parseStatement()方法是解析 @Select/@Update 等注解寫入的 SQL語句,而代碼 GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type) 通過 MaBatis-plus的 SQL 注入器完成 Mapper 方法與 SQL 語句的轉化

  @Override
    public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            loadXmlResource();
            configuration.addLoadedResource(resource);
            String mapperName = type.getName();
            assistant.setCurrentNamespace(mapperName);
            parseCache();
            parseCacheRef();
            InterceptorIgnoreHelper.InterceptorIgnoreCache cache = InterceptorIgnoreHelper.initSqlParserInfoCache(type);
            for (Method method : type.getMethods()) {
                if (!canHaveStatement(method)) {
                    continue;
                }
                if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
                    && method.getAnnotation(ResultMap.class) == null) {
                    parseResultMap(method);
                }
                try {
                    // TODO 加入 注解過濾緩存
                    InterceptorIgnoreHelper.initSqlParserInfoCache(cache, mapperName, method);
                    SqlParserHelper.initSqlParserInfoCache(mapperName, method);
                    parseStatement(method);
                } catch (IncompleteElementException e) {
                    // TODO 使用 MybatisMethodResolver 而不是 MethodResolver
                    configuration.addIncompleteMethod(new MybatisMethodResolver(this, method));
                }
            }
            // TODO 注入 CURD 動態 SQL , 放在在最后, because 可能會有人會用注解重寫sql
            try {
                // https://github.com/baomidou/mybatis-plus/issues/3038
                if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
                    parserInjector();
                }
            } catch (IncompleteElementException e) {
                configuration.addIncompleteMethod(new InjectorResolver(this));
            }
        }
        parsePendingMethods();
    }

6)、AbstractSqlInjector#inspectInject() 會完成 BaseMapper 接口中提供的通用方法對應的 SQL 語句准備,這部分主要通過 AbstractMethod#inject()方法完成

    @Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        Class<?> modelClass = extractModelClass(mapperClass);
        if (modelClass != null) {
            String className = mapperClass.toString();
            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
                List<AbstractMethod> methodList = this.getMethodList(mapperClass);
                if (CollectionUtils.isNotEmpty(methodList)) {
                    TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
                    // 循環注入自定義方法
                    methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
                } else {
                    logger.debug(mapperClass.toString() + ", No effective injection method was found.");
                }
                mapperRegistryCache.add(className);
            }
        }
    }

7)、AbstractMethod#inject()方法並沒有什么特別的操作,只是調用其子類實現 injectMappedStatement()方法。以 SelectOne#injectMappedStatement() 為例,其 SQL 語句的核心在於 SqlMethod 類,這個枚舉類中緩存了可以動態拼接的 SQL 語句腳本,只需要填上參數 format 就可以得到 SQL 語句的執行腳本。以上過程結束,只需要將所有信息通過 addInsertMappedStatement()方法封裝成 MappedStatement對象並將其加入到容器中,這樣 Mapper接口方法調用時,就可以通過 動態代理 的方式找到其對應執行的 SQL 腳本,至此 SQL 語句准備及配置解析就完成了。最后拼接的 SQL 語句 腳本形式如下示例,實際執行數據庫操作時會解析這個腳本完成變量替換,從而得到可執行的 SQL 語句。

<script>
 <choose>
     <when test="ew != null and ew.sqlFirst != null">
         ${ew.sqlFirst}
     </when>
     <otherwise></otherwise>
 </choose>
 SELECT
 <choose>
     <when test="ew != null and ew.sqlSelect != null">
         ${ew.sqlSelect}
     </when>
     <otherwise>id,name,type</otherwise>
 </choose>
 FROM node
 <if test="ew != null">
     <where>
         <if test="ew.entity != null">
             <if test="ew.entity.id != null">id=#{ew.entity.id}</if>
             <if test="ew.entity['name'] != null">AND name=#{ew.entity.name}</if>
             <if test="ew.entity['type'] != null">AND type=#{ew.entity.type}</if>
         </if>
         <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
             <if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal">AND</if>
             ${ew.sqlSegment}
         </if>
     </where>
     <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
         ${ew.sqlSegment}
     </if>
 </if>
 <choose>
     <when test="ew != null and ew.sqlComment != null">
         ${ew.sqlComment}
     </when>
     <otherwise></otherwise>
 </choose>
</script>
@Override
 public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
     SqlMethod sqlMethod = SqlMethod.SELECT_ONE;
     SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(sqlMethod.getSql(),
         sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
         sqlWhereEntityWrapper(true, tableInfo), sqlComment()), modelClass);
     return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
 }

8)、SqlSessionFactory對象的創建需要回到 MybatisSqlSessionFactoryBean#buildSqlSessionFactory()方法中,很容易追蹤到 MybatisSqlSessionFactoryBuilder#build()方法,最后其實是通過 SqlSessionFactoryBuilder#build()方法創建了一個 DefaultSqlSessionFactory 對象返回

public SqlSessionFactory build(Configuration config) {
 return new DefaultSqlSessionFactory(config);
}

2. Mapper 操作數據庫的流程
1)、@MapperScan 注解通過 @Import(MapperScannerRegistrar.class) 引入掃描注冊的類MapperScannerRegistrar,該類實現了ImportBeanDefinitionRegistrar接口並重寫registerBeanDefinitions()方法,在該方法中注冊了 MapperScannerConfigurer 類

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

            if (!AutoConfigurationPackages.has(this.beanFactory)) {
                logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
                return;
            }

            logger.debug("Searching for mappers annotated with @Mapper");

            List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
            if (logger.isDebugEnabled()) {
                packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
            }

            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
            builder.addPropertyValue("processPropertyPlaceHolders", true);
            builder.addPropertyValue("annotationClass", Mapper.class);
            builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
            BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
            Stream.of(beanWrapper.getPropertyDescriptors())
                // Need to mybatis-spring 2.0.2+
                .filter(x -> x.getName().equals("lazyInitialization")).findAny()
                .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));
            registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
        }

2)、MapperScannerConfigurer是 Mapper接口的掃描配置類,實現了 BeanDefinitionRegistryPostProcessor 接口,其 postProcessBeanDefinitionRegistry()方法會在容器啟動過程中被回調,通過 ClassPathMapperScanner#scan()方法完成 Mapper 的掃描注冊

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    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);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    if (StringUtils.hasText(defaultScope)) {
      scanner.setDefaultScope(defaultScope);
    }
    scanner.registerFilters();
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

3)、ClassPathMapperScanner#processBeanDefinitions() 將掃描到的 Mapper接口生成的對應 BeanDefinition 的 beanClass 屬性替換為 MapperFactoryBean,這樣每次獲取 Mapper 實例實際是通過 MapperFactoryBean 的實例去獲取
此處體現了 FactoryBean 的定位,即用於獲取同一類 bean 的工廠 bean

 private void processBeanDefinitions(Set<BeanDefinitionHolder> 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");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      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;
      }

      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());
      }

    }
  }

4)、@Autowired 自動注入 Mapper 觸發容器獲取 bean 的方法,調用到 MapperFactoryBean#getObject()方法,最終調用到 sqlSessionTemplate#getMapper()方法

@Override
public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
}

5)、MyBatis-plus 使用的配置類是MybatisConfiguration,最終調用到 MybatisMapperRegistry#getMapper()方法,這里就進入了動態代理獲取 MapperProxy 實例的流程

@Override
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     // TODO 這里換成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
     final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
     if (mapperProxyFactory == null) {
         throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
     }
     try {
         return mapperProxyFactory.newInstance(sqlSession);
     } catch (Exception e) {
         throw new BindingException("Error getting mapper instance. Cause: " + e, e);
     }
 }

6)、MybatisMapperProxyFactory#newInstance()方法給自動注入返回一個 MybatisMapperProxy 代理對象

protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

7)、調用 Mapper 接口的方法觸發代理對象的 MybatisMapperProxy#invoke(),此時根據 Mapper 對象被調用的方法生成 MybatisMapperMethod 對象,通過MybatisMapperMethod#execute()去真正地執行 SQL 語句,從而完成數據庫操作。此后的流程本文就不再分析,具體可參考文章 MyBatis Mapper 簡要總結

@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 (method.isDefault()) {
             return invokeDefaultMethod(proxy, method, args);
         }
     } catch (Throwable t) {
         throw ExceptionUtil.unwrapThrowable(t);
     }
     final MybatisMapperMethod mapperMethod = cachedMapperMethod(method);
     return mapperMethod.execute(sqlSession, args);
 }

總結:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM