美文网首页
mybatis plus的源码分析

mybatis plus的源码分析

作者: 无聊之园 | 来源:发表于2019-11-13 17:35 被阅读0次

    分析的是mybatis plus 2.0的代码,现在mybatis plus都3.0了。

    分析之前,我们想,
    如果要做一个mybatis plus这样的建立在mybatis之上,自动生成crud的框架,则可以从这个MapperStatement入手,解析mapper接口,MapperStatement过程中,自动生成各种crud的MapperStatement加入configuration的变量mappedStatements中,但是mybatis的MapperStatement都是通过xml或者注解解析而来,而mybatis plus是没有注解和xml的。所以,看看发生了什么。

    maven依赖。

    <dependencies>
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>最新版本号</version>
      </dependency>
    </dependencies>
    
    image.png

    看一下这个配置类。
    其实这个配置类,跟mybatis跟spring boot集成的配置的类MybatisAutoConfiguration差不多,只是改了几个类。

    @EnableConfigurationProperties(MybatisPlusProperties.class)
    MybatisPlusProperties注入了application.yml的mybatis plus配置。
    @AutoConfigureAfter(DataSourceAutoConfiguration.class)
    这个跟spring boot集成mybatis的一样。

    @org.springframework.context.annotation.Configuration
    @ConditionalOnClass({SqlSessionFactory.class, MybatisSqlSessionFactoryBean.class})
    @ConditionalOnBean(DataSource.class)
    @EnableConfigurationProperties(MybatisPlusProperties.class)
    @AutoConfigureAfter(DataSourceAutoConfiguration.class)
    public class MybatisPlusAutoConfiguration {
    

    看一下改了什么

    @Bean
        @ConditionalOnMissingBean
        public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    // 这个换了,原来的是:
    //     SqlSessionFactoryBean factory = new 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()));
            }
            MybatisConfiguration configuration = this.properties.getConfiguration();
            if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
                configuration = new MybatisConfiguration();
            }
            if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
                for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
                    customizer.customize(configuration);
                }
            }
            configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
            factory.setConfiguration(configuration);
            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());
            }
            // TODO 自定义枚举包
            if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
                factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
            }
            if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
                factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
            }
            if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
                factory.setMapperLocations(this.properties.resolveMapperLocations());
            }
            if (!ObjectUtils.isEmpty(this.properties.getGlobalConfig())) {
                factory.setGlobalConfig(this.properties.getGlobalConfig().convertGlobalConfiguration());
            }
            return factory.getObject();
        }
    

    看一下MybatisSqlSessionFactoryBean,这里作者的注释就说了,这个和SqlSessionFactoryBean相差就是buildSqlSessionFactory方法。

    /**
     * <p>
     * 拷贝类 org.mybatis.spring.SqlSessionFactoryBean 修改方法 buildSqlSessionFactory()
     * 加载自定义 MybatisXmlConfigBuilder
     * </p>
     *
     * @author hubin
     * @Date 2017-01-04
     */
    public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    
    

    buildSqlSessionFactory方法替换了很多东西。

     /**
         * Build a {@code SqlSessionFactory} instance.
         * <p>
         * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
         * {@code SqlSessionFactory} instance based on an Reader.
         * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
         *
         * @return SqlSessionFactory
         * @throws IOException if loading the config file failed
         */
        protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    
            Configuration configuration;
          // 这里把原来的XMLConfigBuilder换成了MybatisXmlConfigBuilder,这两
    //个的区别就是MybatisXmlConfigBuilder使用的mybatis plus自己的
    //MybatisConfiguration,XMLConfigBuilder使用的是mybatis 的configuration。
    // 作者在MybatisXmlConfigBuilder注解上有写
            // TODO 加载自定义 MybatisXmlConfigBuilder
            MybatisXMLConfigBuilder xmlConfigBuilder = null;
            if (this.configuration != null) {
                configuration = this.configuration;
                if (configuration.getVariables() == null) {
                    configuration.setVariables(this.configurationProperties);
                } else if (this.configurationProperties != null) {
                    configuration.getVariables().putAll(this.configurationProperties);
                }
            } else if (this.configLocation != null) {
                xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
                configuration = xmlConfigBuilder.getConfiguration();
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
                }
    // 这里改了,原来的是Configuration
                // TODO 使用自定义配置
                configuration = new MybatisConfiguration();
                if (this.configurationProperties != null) {
                    configuration.setVariables(this.configurationProperties);
                }
            }
    
            if (this.objectFactory != null) {
                configuration.setObjectFactory(this.objectFactory);
            }
    
            if (this.objectWrapperFactory != null) {
                configuration.setObjectWrapperFactory(this.objectWrapperFactory);
            }
    
            if (this.vfs != null) {
                configuration.setVfsImpl(this.vfs);
            }
    
            if (hasLength(this.typeAliasesPackage)) {
              // 下面三个if也是新增的,用处作者有注释
                // TODO 支持自定义通配符
                String[] typeAliasPackageArray;
                if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
                    && !typeAliasesPackage.contains(";")) {
                    typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage);
                } else {
                    typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
                        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                }
                if (typeAliasPackageArray == null) {
                    throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage);
                }
                for (String packageToScan : typeAliasPackageArray) {
                    configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                        typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
                    }
                }
            }
    
            // TODO 自定义枚举类扫描处理
            if (hasLength(this.typeEnumsPackage)) {
                Set<Class> classes = null;
                if (typeEnumsPackage.contains("*") && !typeEnumsPackage.contains(",")
                    && !typeEnumsPackage.contains(";")) {
                    classes = PackageHelper.scanTypePackage(typeEnumsPackage);
                } else {
                    String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
                        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                    if (typeEnumsPackageArray == null) {
                        throw new MybatisPlusException("not find typeEnumsPackage:" + typeEnumsPackage);
                    }
                    classes = new HashSet<Class>();
                    for (String typePackage : typeEnumsPackageArray) {
                        classes.addAll(PackageHelper.scanTypePackage(typePackage));
                    }
                }
                // 取得类型转换注册器
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                for (Class cls : classes) {
                    if (cls.isEnum()) {
                        if (IEnum.class.isAssignableFrom(cls)) {
                            typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName());
                        } else {
                            // 使用原生 EnumOrdinalTypeHandler
                            typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName());
                        }
                    }
                }
            }
    
            if (!isEmpty(this.typeAliases)) {
                for (Class<?> typeAlias : this.typeAliases) {
                    configuration.getTypeAliasRegistry().registerAlias(typeAlias);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Registered type alias: '" + typeAlias + "'");
                    }
                }
            }
    
            if (!isEmpty(this.plugins)) {
                for (Interceptor plugin : this.plugins) {
                    configuration.addInterceptor(plugin);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Registered plugin: '" + plugin + "'");
                    }
                }
            }
    
            if (hasLength(this.typeHandlersPackage)) {
                String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                for (String packageToScan : typeHandlersPackageArray) {
                    configuration.getTypeHandlerRegistry().register(packageToScan);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
                    }
                }
            }
    
            if (!isEmpty(this.typeHandlers)) {
                for (TypeHandler<?> typeHandler : this.typeHandlers) {
                    configuration.getTypeHandlerRegistry().register(typeHandler);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Registered type handler: '" + typeHandler + "'");
                    }
                }
            }
    
            if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
                try {
                    configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
                } catch (SQLException e) {
                    throw new NestedIOException("Failed getting a databaseId", e);
                }
            }
    
            if (this.cache != null) {
                configuration.addCache(this.cache);
            }
    
            if (xmlConfigBuilder != null) {
                try {
                    xmlConfigBuilder.parse();
    
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
                    }
                } catch (Exception ex) {
                    throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
                } finally {
                    ErrorContext.instance().reset();
                }
            }
    
            if (this.transactionFactory == null) {
                this.transactionFactory = new SpringManagedTransactionFactory();
            }
    
            configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
    // GlobalConfigUtils是mybatis plus的全局的缓存类
            // 设置元数据相关
            GlobalConfigUtils.setMetaData(dataSource, globalConfig);
            SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
            // SqlRunner是mybatis plus的执行sql的类
            // TODO SqlRunner
            SqlRunner.FACTORY = sqlSessionFactory;
            // TODO 缓存 sqlSessionFactory
            globalConfig.setSqlSessionFactory(sqlSessionFactory);
            // TODO 设置全局参数属性
            globalConfig.signGlobalConfig(sqlSessionFactory);
    // 下面是解析mapper了,和mybatis差不多,没什么大变化,就是正常的解析mapper.xml文件
            if (!isEmpty(this.mapperLocations)) {
                if (globalConfig.isRefresh()) {
                    //TODO 设置自动刷新配置 减少配置
                    new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
                        2, true);
                }
                for (Resource mapperLocation : this.mapperLocations) {
                    if (mapperLocation == null) {
                        continue;
                    }
    
                    try {
                        // TODO  这里也换了噢噢噢噢
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                            configuration, mapperLocation.toString(), configuration.getSqlFragments());
                        xmlMapperBuilder.parse();
                    } catch (Exception e) {
                        throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                    } finally {
                        ErrorContext.instance().reset();
                    }
    
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                    }
                }
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
                }
            }
            return sqlSessionFactory;
        }
    

    可见,到这里,如果mybatis plus没有xml文件,那么到这里,都没有生成mapper的代理类,也没有生成mapperStatement对象注入configuration中。

    那么其实,生成mapper代理类,并且自动生成CRUD 的mapperStatement对象的逻辑,在@MapperScan注解中

    看这个mybatis plus的配置类,使用的MapperScan注解是mybatis的注解。

    @Configuration
    @MapperScan(basePackages = {"com.mit.community.mapper", "com.mit.community.*.*.mapper"})
    public class MybatisPlusConfig {
    

    同样,看MapperScannerRegistrar这个类

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(MapperScannerRegistrar.class)
    public @interface MapperScan {
    

    省略MapperScannerRegistrar的registerBeanDefinitions方法的内部的详情,之前分析过,关注重点
    最终doScan方法会调用下面的方法,修改注入到spring中的mapper的bean。

           definition.setBeanClass(this.mapperFactoryBean.getClass());
    

    看mapperFactoryBean这个类。

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
    

    MapperFactoryBean是DaoSupport的子类的子类,而DaoSupport实现了InitializingBean 接口,所以自然,服务器一启动,就会调用InitializingBeanafterPropertiesSet方法。

    public abstract class DaoSupport implements InitializingBean 
    

    这个方法,会调用抽象方法checkDaoConfig,子类实现checkDaoConfig这个方法,模板方法设计模式。

    @Override
        public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
            // Let abstract subclasses check their configuration.
            checkDaoConfig();
    
            // Let concrete implementations initialize themselves.
            try {
                initDao();
            }
            catch (Exception ex) {
                throw new BeanInitializationException("Initialization of DAO failed", ex);
            }
        }
    

    然后看一下MapperFactoryBean的实现的checkDaoConfig

     @Override
      protected void checkDaoConfig() {
        super.checkDaoConfig();
    
        notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    // 这里获取到的Configuration是mybatis plus的MybatisConfiguration
        Configuration configuration = getSqlSession().getConfiguration();
    // 如果Configuration中没有注入过这个mapper,则从新注入。这里,自然前面没有注入过,所以会走入if里。
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
          // 执行MybatisConfiguration的addMapper方法,MybatisConfiguration重写了addMapper方法。
            configuration.addMapper(this.mapperInterface);
          } catch (Exception e) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
            throw new IllegalArgumentException(e);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    

    MybatisConfiguration的addMapper方法

    mybatis plus重写了mybatisMapperRegistry

    
        /**
         * Mapper 注册
         */
        public final mybatisMapperRegistry = new MybatisMapperRegistry(this);
    
      @Override
        public <T> void addMapper(Class<T> type) {
            mybatisMapperRegistry.addMapper(type);
        }
    

    mybatisMapperRegistry.addMapper(type)方法

     @Override
        public <T> void addMapper(Class<T> type) {
            if (type.isInterface()) {
                if (hasMapper(type)) {
                    // TODO 如果之前注入 直接返回
                    return;
                    // throw new BindingException("Type " + type +
                    // " is already known to the MybatisPlusMapperRegistry.");
                }
                boolean loadCompleted = false;
                try {
                    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.
                    // TODO 自定义无 XML 注入
    // mybtais plus的MybatisMapperAnnotationBuilder,下面就是生成CRUD的maperStatement了 
                    MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
                    parser.parse();
                    loadCompleted = true;
                } finally {
                    if (!loadCompleted) {
                        knownMappers.remove(type);
                    }
                }
            }
        }
    
    @Override
        public void parse() {
            String resource = type.toString();
            if (!configuration.isResourceLoaded(resource)) {
                loadXmlResource();
                configuration.addLoadedResource(resource);
                assistant.setCurrentNamespace(type.getName());
                parseCache();
                parseCacheRef();
                Method[] methods = type.getMethods();
                // TODO 注入 CURD 动态 SQL (应该在注解之前注入)
              // 如果mapper接口实现了BaseMapper接口,则生成CRUD的mapperStatement自动注入 
                if (BaseMapper.class.isAssignableFrom(type)) {
      // 从全局缓存中取SqlInjector,这在注入mybatis plus的时候,已经注入进去了,前面说过           GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
                }
                for (Method method : methods) {
                    try {
                        // issue #237
                        if (!method.isBridge()) {
                            parseStatement(method);
                        }
                    } catch (IncompleteElementException e) {
                        configuration.addIncompleteMethod(new MethodResolver(this, method));
                    }
                }
            }
            parsePendingMethods();
        }
    
     /**
         * <p>
         * CRUD 注入后给予标识 注入过后不再注入
         * </p>
         *
         * @param builderAssistant
         * @param mapperClass
         */
        @Override
        public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
            String className = mapperClass.toString();
            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
                inject(builderAssistant, mapperClass);
                mapperRegistryCache.add(className);
            }
        }
    

    AutoSqlInjector的inject方法,注入

     /**
         * 注入单点 crudSql
         */
        @Override
        public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
            this.configuration = builderAssistant.getConfiguration();
            this.builderAssistant = builderAssistant;
            this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
    
            //去除 驼峰设置 PLUS 配置 > 原生配置 (该配置不需要与原生Mybatis混淆)
            /*if (!globalCache.isDbColumnUnderline()) {
                globalCache.setDbColumnUnderline(configuration.isMapUnderscoreToCamelCase());
            }*/
    // 获取mapper的泛型,因为必须要直到生成哪个类的crud MapperStatement
            Class<?> modelClass = extractModelClass(mapperClass);
            if (null != modelClass) {
                /**
                 * 初始化 SQL 解析
                 */
                if (this.getGlobalConfig().isSqlParserCache()) {
                    PluginUtils.initSqlParserInfoCache(mapperClass);
                }
              // 分析泛型中关于表的注解的信息,最终生成TableInfo对象,tableInfo对象包含了生成CRUD的所有数据库表信息
                TableInfo table = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
    // 之后,生成
                injectSql(builderAssistant, mapperClass, modelClass, table);
            }
        }
    

    看一下注入sql

     /**
         * <p>
         * 注入SQL
         * </p>
         *
         * @param builderAssistant
         * @param mapperClass
         * @param modelClass
         * @param table
         */
        protected void injectSql(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
            /**
             * #148 表信息包含主键,注入主键相关方法
             */
            if (StringUtils.isNotEmpty(table.getKeyProperty())) {
                /** 删除 */
                this.injectDeleteByIdSql(false, mapperClass, modelClass, table);
                this.injectDeleteByIdSql(true, mapperClass, modelClass, table);
                /** 修改 */
                this.injectUpdateByIdSql(true, mapperClass, modelClass, table);
                this.injectUpdateByIdSql(false, mapperClass, modelClass, table);
                /** 查询 */
                this.injectSelectByIdSql(false, mapperClass, modelClass, table);
                this.injectSelectByIdSql(true, mapperClass, modelClass, table);
            } else {
                // 表不包含主键时 给予警告
                logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
                    modelClass.toString()));
            }
            /**
             * 正常注入无需主键方法
             */
            /** 插入 */
            this.injectInsertOneSql(true, mapperClass, modelClass, table);
            this.injectInsertOneSql(false, mapperClass, modelClass, table);
            /** 删除 */
            this.injectDeleteSql(mapperClass, modelClass, table);
            this.injectDeleteByMapSql(mapperClass, table);
            /** 修改 */
            this.injectUpdateSql(mapperClass, modelClass, table);
            /** 修改 (自定义 set 属性) */
            this.injectUpdateForSetSql(mapperClass, modelClass, table);
            /** 查询 */
            this.injectSelectByMapSql(mapperClass, modelClass, table);
            this.injectSelectOneSql(mapperClass, modelClass, table);
            this.injectSelectCountSql(mapperClass, modelClass, table);
            this.injectSelectListSql(SqlMethod.SELECT_LIST, mapperClass, modelClass, table);
            this.injectSelectListSql(SqlMethod.SELECT_PAGE, mapperClass, modelClass, table);
            this.injectSelectMapsSql(SqlMethod.SELECT_MAPS, mapperClass, modelClass, table);
            this.injectSelectMapsSql(SqlMethod.SELECT_MAPS_PAGE, mapperClass, modelClass, table);
            this.injectSelectObjsSql(SqlMethod.SELECT_OBJS, mapperClass, modelClass, table);
            /** 自定义方法 */
            this.inject(configuration, builderAssistant, mapperClass, modelClass, table);
        }
    

    找一个删除的sql注入分析。

     /**
         * <p>
         * 注入删除 SQL 语句
         * </p>
         *
         * @param mapperClass
         * @param modelClass
         * @param table
         */
        protected void injectDeleteByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
          // SqlMethod 是一个枚举,里面定义了各个crud等方法的sql语句,你会发现里面的sql语句都是<script></script>包裹的,但是其实原生的mybatis是<select><insert>等包裹的xnode节点。
    // 看    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);就会发现,里面是会解析<script>的节点的
            SqlMethod sqlMethod = SqlMethod.DELETE_BY_ID;
            SqlSource sqlSource;
            // 因为后面要通过get方法获取类型,所以这里要获取key的属性值
            String idStr = table.getKeyProperty();
            if (batch) {
                sqlMethod = SqlMethod.DELETE_BATCH_BY_IDS;
                StringBuilder ids = new StringBuilder();
                ids.append("\n<foreach item=\"item\" index=\"index\" collection=\"coll\" separator=\",\">");
                ids.append("#{item}");
                ids.append("\n</foreach>");
                idStr = ids.toString();
            }
    // 替换一下sql中的参数
            String sql = String.format(sqlMethod.getSql(), table.getTableName(), table.getKeyColumn(), idStr);
    // 生成mybatis的SqlSource
            sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
    // 构建MappedStatement并添加到configuration中
            this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
        }
    

    至此,就分析完了。

    大概总结一下:替换了Configuration为MybatisConfiguration,MybatisConfiguration的mapperRegistry变量替换成MybatisMapperRegistry。
    这样当@MapperScan注解导入的处理类MapperScannerRegistrar注入了mapperFactoryBean工厂类之后,这个工厂类,父类实现了 InitializingBean接口,所以服务器一启动,就会调用mapperFactoryBean的checkDaoConfig方法,判断mapper接口是否被注入到configuration中,自然是没有的,所以会调用替换过的MybatisConfiguration的addMapper方法注入到configuration中,再这个注入过程中,生成CRUD等MapperStatement,然后注入到Configuration中。

    相关文章

      网友评论

          本文标题:mybatis plus的源码分析

          本文链接:https://www.haomeiwen.com/subject/zqxkictx.html