zoukankan      html  css  js  c++  java
  • MyBatisplus源码解析

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

    总结:

    郭慕荣博客园
  • 相关阅读:
    Windows 服务程序(一)
    API---注册表编程
    API---文件操作
    main(argc, char *argv[])
    C 自删除技术---批处理方式
    分治法排序
    TDD尝试:nodejs单元测试
    尝试create tech team
    Yum重装走过的坑
    求生欲很强的数据库
  • 原文地址:https://www.cnblogs.com/jelly12345/p/15628277.html
Copyright © 2011-2022 走看看