mybatis plus的源码分析

2019-11-13  本文已影响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中。

上一篇下一篇

猜你喜欢

热点阅读