• mybatis 增加热加载xml


      由于在本地开发环境上每次修改mybatis xml文件都需要手动重启服务,调试的很麻烦,所以需要热加载xml文件来避免浪费时间,于是网上搜一下资料,看了下有一大堆,但试了下真正能跑起来没有(大都代码没给全),故参考了改了下。

    首先确定 mybatis 修改的类:

      XMLMapperBuilder(默认xml加载后不会再次加载了,修改此逻辑)
      Configuration(重写StrictMap put方法,删除原来加载的数据,重新加载)
      SqlSessionFactoryBean(启动刷新线程)

     1、在 src 下面建立与 myabtis 上述相同目录的类(tomcat 启动的时候会先加载classes目录下的文件,然后才会加载lib下面的jar包,根据类加载机制,加载了src下面同目录后的类就不会再加载jar包里面的类了)。
     2、代码如下:
    /*
     *    Copyright 2010-2013 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.mybatis.spring;
    
    import org.apache.ibatis.builder.xml.XMLConfigBuilder;
    import org.apache.ibatis.builder.xml.XMLMapperBuilder;
    import org.apache.ibatis.executor.ErrorContext;
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.logging.LogFactory;
    import org.apache.ibatis.mapping.DatabaseIdProvider;
    import org.apache.ibatis.mapping.Environment;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.apache.ibatis.transaction.TransactionFactory;
    import org.apache.ibatis.type.TypeHandler;
    import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.core.NestedIOException;
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
    import org.springframework.util.ClassUtils;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.sql.SQLException;
    import java.util.HashSet;
    import java.util.Properties;
    import java.util.Set;
    
    import static org.springframework.util.Assert.notNull;
    import static org.springframework.util.ObjectUtils.isEmpty;
    import static org.springframework.util.StringUtils.hasLength;
    import static org.springframework.util.StringUtils.tokenizeToStringArray;
    
    /**
     * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}. This
     * is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a
     * Spring application context; the SqlSessionFactory can then be passed to
     * MyBatis-based DAOs via dependency injection.
     * 
     * Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager}
     * can be used for transaction demarcation in combination with a
     * {@code SqlSessionFactory}. JTA should be used for transactions which span
     * multiple databases or when container managed transactions (CMT) are being
     * used.
     * 
     * @author Putthibong Boonbong
     * @author Hunter Presnall
     * @author Eduardo Macarron
     * 
     * @see #setConfigLocation
     * @see #setDataSource
     * @version $Id$
     * @desctiption 刷新xml文件
     */
    public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>,
            InitializingBean, ApplicationListener<ApplicationEvent> {
    
        private static final Log logger = LogFactory
                .getLog(SqlSessionFactoryBean.class);
    
        private Resource configLocation;
    
        private Resource[] mapperLocations;
    
        private DataSource dataSource;
    
        private TransactionFactory transactionFactory;
    
        private Properties configurationProperties;
    
        private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    
        private SqlSessionFactory sqlSessionFactory;
    
        private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1
    
        private boolean failFast;
    
        private Interceptor[] plugins;
    
        private TypeHandler<?>[] typeHandlers;
    
        private String typeHandlersPackage;
    
        private Class<?>[] typeAliases;
    
        private String typeAliasesPackage;
    
        private Class<?> typeAliasesSuperType;
    
        private DatabaseIdProvider databaseIdProvider; // issue #19. No default provider.
    
        private ObjectFactory objectFactory;
    
        private ObjectWrapperFactory objectWrapperFactory;
    
        /**
         * Sets the ObjectFactory.
         * 
         * @since 1.1.2
         * @param objectFactory
         */
        public void setObjectFactory(ObjectFactory objectFactory) {
            this.objectFactory = objectFactory;
        }
    
        /**
         * Sets the ObjectWrapperFactory.
         * 
         * @since 1.1.2
         * @param objectWrapperFactory
         */
        public void setObjectWrapperFactory(
                ObjectWrapperFactory objectWrapperFactory) {
            this.objectWrapperFactory = objectWrapperFactory;
        }
    
        /**
         * Gets the DatabaseIdProvider
         * 
         * @since 1.1.0
         * @return
         */
        public DatabaseIdProvider getDatabaseIdProvider() {
            return databaseIdProvider;
        }
    
        /**
         * Sets the DatabaseIdProvider. As of version 1.2.2 this variable is not
         * initialized by default.
         * 
         * @since 1.1.0
         * @param databaseIdProvider
         */
        public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
            this.databaseIdProvider = databaseIdProvider;
        }
    
        /**
         * Mybatis plugin list.
         * 
         * @since 1.0.1
         * 
         * @param plugins
         *            list of plugins
         * 
         */
        public void setPlugins(Interceptor[] plugins) {
            this.plugins = plugins;
        }
    
        /**
         * Packages to search for type aliases.
         * 
         * @since 1.0.1
         * 
         * @param typeAliasesPackage
         *            package to scan for domain objects
         * 
         */
        public void setTypeAliasesPackage(String typeAliasesPackage) {
            this.typeAliasesPackage = typeAliasesPackage;
        }
    
        /**
         * Super class which domain objects have to extend to have a type alias
         * created. No effect if there is no package to scan configured.
         * 
         * @since 1.1.2
         * 
         * @param typeAliasesSuperType
         *            super class for domain objects
         * 
         */
        public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
            this.typeAliasesSuperType = typeAliasesSuperType;
        }
    
        /**
         * Packages to search for type handlers.
         * 
         * @since 1.0.1
         * 
         * @param typeHandlersPackage
         *            package to scan for type handlers
         * 
         */
        public void setTypeHandlersPackage(String typeHandlersPackage) {
            this.typeHandlersPackage = typeHandlersPackage;
        }
    
        /**
         * Set type handlers. They must be annotated with {@code MappedTypes} and
         * optionally with {@code MappedJdbcTypes}
         * 
         * @since 1.0.1
         * 
         * @param typeHandlers
         *            Type handler list
         */
        public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
            this.typeHandlers = typeHandlers;
        }
    
        /**
         * List of type aliases to register. They can be annotated with
         * {@code Alias}
         * 
         * @since 1.0.1
         * 
         * @param typeAliases
         *            Type aliases list
         */
        public void setTypeAliases(Class<?>[] typeAliases) {
            this.typeAliases = typeAliases;
        }
    
        /**
         * If true, a final check is done on Configuration to assure that all mapped
         * statements are fully loaded and there is no one still pending to resolve
         * includes. Defaults to false.
         * 
         * @since 1.0.1
         * 
         * @param failFast
         *            enable failFast
         */
        public void setFailFast(boolean failFast) {
            this.failFast = failFast;
        }
    
        /**
         * Set the location of the MyBatis {@code SqlSessionFactory} config file. A
         * typical value is "WEB-INF/mybatis-configuration.xml".
         */
        public void setConfigLocation(Resource configLocation) {
            this.configLocation = configLocation;
        }
    
        /**
         * Set locations of MyBatis mapper files that are going to be merged into
         * the {@code SqlSessionFactory} configuration at runtime.
         * 
         * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an
         * MyBatis config file. This property being based on Spring's resource
         * abstraction also allows for specifying resource patterns here: e.g.
         * "classpath*:sqlmap/*-mapper.xml".
         */
        public void setMapperLocations(Resource[] mapperLocations) {
            this.mapperLocations = mapperLocations;
        }
    
        /**
         * Set optional properties to be passed into the SqlSession configuration,
         * as alternative to a {@code &lt;properties&gt;} tag in the configuration
         * xml file. This will be used to resolve placeholders in the config file.
         */
        public void setConfigurationProperties(
                Properties sqlSessionFactoryProperties) {
            this.configurationProperties = sqlSessionFactoryProperties;
        }
    
        /**
         * Set the JDBC {@code DataSource} that this instance should manage
         * transactions for. The {@code DataSource} should match the one used by the
         * {@code SqlSessionFactory}: for example, you could specify the same JNDI
         * DataSource for both.
         * 
         * A transactional JDBC {@code Connection} for this {@code DataSource} will
         * be provided to application code accessing this {@code DataSource}
         * directly via {@code DataSourceUtils} or
         * {@code DataSourceTransactionManager}.
         * 
         * The {@code DataSource} specified here should be the target
         * {@code DataSource} to manage transactions for, not a
         * {@code TransactionAwareDataSourceProxy}. Only data access code may work
         * with {@code TransactionAwareDataSourceProxy}, while the transaction
         * manager needs to work on the underlying target {@code DataSource}. If
         * there's nevertheless a {@code TransactionAwareDataSourceProxy} passed in,
         * it will be unwrapped to extract its target {@code DataSource}.
         * 
         */
        public void setDataSource(DataSource dataSource) {
            if (dataSource instanceof TransactionAwareDataSourceProxy) {
                // If we got a TransactionAwareDataSourceProxy, we need to perform
                // transactions for its underlying target DataSource, else data
                // access code won't see properly exposed transactions (i.e.
                // transactions for the target DataSource).
                this.dataSource = ((TransactionAwareDataSourceProxy) dataSource)
                        .getTargetDataSource();
            } else {
                this.dataSource = dataSource;
            }
        }
    
        /**
         * Sets the {@code SqlSessionFactoryBuilder} to use when creating the
         * {@code SqlSessionFactory}.
         * 
         * This is mainly meant for testing so that mock SqlSessionFactory classes
         * can be injected. By default, {@code SqlSessionFactoryBuilder} creates
         * {@code DefaultSqlSessionFactory} instances.
         * 
         */
        public void setSqlSessionFactoryBuilder(
                SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
            this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
        }
    
        /**
         * Set the MyBatis TransactionFactory to use. Default is
         * {@code SpringManagedTransactionFactory}
         * 
         * The default {@code SpringManagedTransactionFactory} should be appropriate
         * for all cases: be it Spring transaction management, EJB CMT or plain JTA.
         * If there is no active transaction, SqlSession operations will execute SQL
         * statements non-transactionally.
         * 
         * <b>It is strongly recommended to use the default
         * {@code TransactionFactory}.</b> If not used, any attempt at getting an
         * SqlSession through Spring's MyBatis framework will throw an exception if
         * a transaction is active.
         * 
         * @see SpringManagedTransactionFactory
         * @param transactionFactory
         *            the MyBatis TransactionFactory
         */
        public void setTransactionFactory(TransactionFactory transactionFactory) {
            this.transactionFactory = transactionFactory;
        }
    
        /**
         * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you
         * have set in the MyBatis config file. This is used only as a placeholder
         * name. The default value is
         * {@code SqlSessionFactoryBean.class.getSimpleName()}.
         * 
         * @param environment
         *            the environment name
         */
        public void setEnvironment(String environment) {
            this.environment = environment;
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            notNull(dataSource, "Property 'dataSource' is required");
            notNull(sqlSessionFactoryBuilder,
                    "Property 'sqlSessionFactoryBuilder' is required");
    
            this.sqlSessionFactory = buildSqlSessionFactory();
        }
    
        /**
         * Build a {@code SqlSessionFactory} instance.
         * 
         * The default implementation uses the standard MyBatis
         * {@code XMLConfigBuilder} API to build a {@code SqlSessionFactory}
         * instance based on an Reader.
         * 
         * @return SqlSessionFactory
         * @throws IOException
         *             if loading the config file failed
         */
        protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    
            Configuration configuration;
    
            XMLConfigBuilder xmlConfigBuilder = null;
            if (this.configLocation != null) {
                xmlConfigBuilder = new XMLConfigBuilder(
                        this.configLocation.getInputStream(), null,
                        this.configurationProperties);
                configuration = xmlConfigBuilder.getConfiguration();
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
                }
                configuration = new Configuration();
                configuration.setVariables(this.configurationProperties);
            }
    
            if (this.objectFactory != null) {
                configuration.setObjectFactory(this.objectFactory);
            }
    
            if (this.objectWrapperFactory != null) {
                configuration.setObjectWrapperFactory(this.objectWrapperFactory);
            }
    
            if (hasLength(this.typeAliasesPackage)) {
                // TODO 支持自定义通配符
                String[] typeAliasPackageArray;
                if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
                        && !typeAliasesPackage.contains(";")) {
                    typeAliasPackageArray = convertTypeAliasesPackage(typeAliasesPackage);
                } else {
                    typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
                            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                }
                if (typeAliasPackageArray == null) {
                    throw new IOException("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");
                    }
                }
            }
    
    
            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 (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();
            }
    
            Environment environment = new Environment(this.environment,
                    this.transactionFactory, this.dataSource);
            configuration.setEnvironment(environment);
    
            if (this.databaseIdProvider != null) {
                try {
                    configuration.setDatabaseId(this.databaseIdProvider
                            .getDatabaseId(this.dataSource));
                } catch (SQLException e) {
                    throw new NestedIOException("Failed getting a databaseId", e);
                }
            }
            String location = null;
            if (!isEmpty(this.mapperLocations)) {
                for (Resource mapperLocation : this.mapperLocations) {
                    if (location == null && mapperLocation instanceof FileSystemResource) {
                        location = mapperLocation.toString();
                    }
                    if (mapperLocation == null) {
                        continue;
                    }
    
                    try {
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
                                mapperLocation.getInputStream(), configuration,
                                mapperLocation.toString(),
                                configuration.getSqlFragments());
                        xmlMapperBuilder.parse();
                    } catch (Exception e) {
                        e.printStackTrace(); // 出现错误抛出异常
                        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");
                }
            }
    
            //启动定时器
            new org.apache.ibatis.thread.RefreshRunnable(mapperLocations, configuration).run();
    
            return this.sqlSessionFactoryBuilder.build(configuration);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public SqlSessionFactory getObject() throws Exception {
            if (this.sqlSessionFactory == null) {
                afterPropertiesSet();
            }
    
            return this.sqlSessionFactory;
        }
    
        /**
         * {@inheritDoc}
         */
        public Class<? extends SqlSessionFactory> getObjectType() {
            return this.sqlSessionFactory == null ? SqlSessionFactory.class
                    : this.sqlSessionFactory.getClass();
        }
    
        /**
         * {@inheritDoc}
         */
        public boolean isSingleton() {
            return true;
        }
    
        /**
         * {@inheritDoc}
         */
        public void onApplicationEvent(ApplicationEvent event) {
            if (failFast && event instanceof ContextRefreshedEvent) {
                // fail-fast -> check all statements are completed
                this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
            }
        }
    
        /**
         * 重新加载 xmlMapper
         * @param inputStream
         * @param resource
         * @param configuration
         * @throws NestedIOException
         */
        public static void refresh(java.io.InputStream inputStream,
                String resource, Configuration configuration)
                throws NestedIOException {
            try {
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
                        inputStream, configuration, resource,
                        configuration.getSqlFragments());
                xmlMapperBuilder.parse1();
            } catch (Exception e) {
                throw new NestedIOException("Failed to parse mapping resource: '"
                        + resource + "'", e);
            } finally {
                ErrorContext.instance().reset();
            }
    
        }
    
    
        public static String[] convertTypeAliasesPackage(String typeAliasesPackage) throws IOException{
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
            String pkg = "classpath*:" + ClassUtils.convertClassNameToResourcePath(typeAliasesPackage) + "/*.class";
    
            try {
                Set<String> set = new HashSet<>();
                Resource[] resources = resolver.getResources(pkg);
                if(resources != null && resources.length > 0) {
                    Resource[] arr = resources;
                    int len = resources.length;
    
                    for(int i = 0; i < len; ++i) {
                        Resource resource = arr[i];
                        if(resource.isReadable()) {
                            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                            set.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
                        }
                    }
                }
    
                if(!set.isEmpty()) {
                    return set.toArray(new String[0]);
                } else {
                    throw new IOException("not find typeAliasesPackage:" + pkg);
                }
            } catch (Exception var11) {
                throw new IOException("not find typeAliasesPackage:" + pkg);
            }
        }
    
    
    }
    /*
     *    Copyright 2009-2014 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.session;
    
    import org.apache.ibatis.binding.MapperRegistry;
    import org.apache.ibatis.builder.CacheRefResolver;
    import org.apache.ibatis.builder.ResultMapResolver;
    import org.apache.ibatis.builder.annotation.MethodResolver;
    import org.apache.ibatis.builder.xml.XMLStatementBuilder;
    import org.apache.ibatis.cache.Cache;
    import org.apache.ibatis.cache.decorators.FifoCache;
    import org.apache.ibatis.cache.decorators.LruCache;
    import org.apache.ibatis.cache.decorators.SoftCache;
    import org.apache.ibatis.cache.decorators.WeakCache;
    import org.apache.ibatis.cache.impl.PerpetualCache;
    import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
    import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
    import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
    import org.apache.ibatis.executor.*;
    import org.apache.ibatis.executor.keygen.KeyGenerator;
    import org.apache.ibatis.executor.loader.ProxyFactory;
    import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
    import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
    import org.apache.ibatis.executor.parameter.ParameterHandler;
    import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
    import org.apache.ibatis.executor.resultset.ResultSetHandler;
    import org.apache.ibatis.executor.statement.RoutingStatementHandler;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.logging.LogFactory;
    import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
    import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
    import org.apache.ibatis.logging.log4j.Log4jImpl;
    import org.apache.ibatis.logging.log4j2.Log4j2Impl;
    import org.apache.ibatis.logging.nologging.NoLoggingImpl;
    import org.apache.ibatis.logging.slf4j.Slf4jImpl;
    import org.apache.ibatis.logging.stdout.StdOutImpl;
    import org.apache.ibatis.mapping.*;
    import org.apache.ibatis.parsing.XNode;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.InterceptorChain;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
    import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
    import org.apache.ibatis.scripting.LanguageDriver;
    import org.apache.ibatis.scripting.LanguageDriverRegistry;
    import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
    import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
    import org.apache.ibatis.transaction.Transaction;
    import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
    import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.TypeAliasRegistry;
    import org.apache.ibatis.type.TypeHandlerRegistry;
    
    import java.util.*;
    
    /**
     * @author Clinton Begin
     * @description 重写put,实现刷新的功能
     */
    public class Configuration {
    
        protected Environment environment;
    
        protected boolean safeRowBoundsEnabled = false;
        protected boolean safeResultHandlerEnabled = true;
        protected boolean mapUnderscoreToCamelCase = false;
        protected boolean aggressiveLazyLoading = true;
        protected boolean multipleResultSetsEnabled = true;
        protected boolean useGeneratedKeys = false;
        protected boolean useColumnLabel = true;
        protected boolean cacheEnabled = true;
        protected boolean callSettersOnNulls = false;
        protected String logPrefix;
        protected Class<? extends Log> logImpl;
        protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
        protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
        protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(
                Arrays.asList("equals", "clone", "hashCode",
                        "toString"));
        protected Integer defaultStatementTimeout;
        protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
        protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    
        protected Properties variables = new Properties();
        protected ObjectFactory objectFactory = new DefaultObjectFactory();
        protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
        protected MapperRegistry mapperRegistry = new MapperRegistry(this);
    
        protected boolean lazyLoadingEnabled = false;
        protected ProxyFactory proxyFactory;
    
        protected String databaseId;
        /**
         * Configuration factory class. Used to create Configuration for loading
         * deserialized unread properties.
         * 
         * @see <a
         *      href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue
         *      300</a> (google code)
         */
        protected Class<?> configurationFactory;
    
        protected final InterceptorChain interceptorChain = new InterceptorChain();
        protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
        protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
        protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
        protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
                "Mapped Statements collection");
        protected final Map<String, Cache> caches = new StrictMap<Cache>(
                "Caches collection");
        protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>(
                "Result Maps collection");
        protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>(
                "Parameter Maps collection");
        protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>(
                "Key Generators collection");
    
        protected final Set<String> loadedResources = new HashSet<String>();
        protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>(
                "XML fragments parsed from previous mappers");
    
        protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
        protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
        protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
        protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
    
        /*
         * A map holds cache-ref relationship. The key is the namespace that
         * references a cache bound to another namespace and the value is the
         * namespace which the actual cache is bound to.
         */
        protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
    
        public Configuration(Environment environment) {
            this();
            this.environment = environment;
        }
    
        public Configuration() {
            typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
            typeAliasRegistry.registerAlias("MANAGED",
                    ManagedTransactionFactory.class);
    
            typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
            typeAliasRegistry
                    .registerAlias("POOLED", PooledDataSourceFactory.class);
            typeAliasRegistry.registerAlias("UNPOOLED",
                    UnpooledDataSourceFactory.class);
    
            typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
            typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
            typeAliasRegistry.registerAlias("LRU", LruCache.class);
            typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
            typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    
            typeAliasRegistry.registerAlias("DB_VENDOR",
                    VendorDatabaseIdProvider.class);
    
            typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
            typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    
            typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
            typeAliasRegistry.registerAlias("COMMONS_LOGGING",
                    JakartaCommonsLoggingImpl.class);
            typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
            typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
            typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
            typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
            typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    
            typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
            typeAliasRegistry.registerAlias("JAVASSIST",
                    JavassistProxyFactory.class);
    
            languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
            languageRegistry.register(RawLanguageDriver.class);
        }
    
        public String getLogPrefix() {
            return logPrefix;
        }
    
        public void setLogPrefix(String logPrefix) {
            this.logPrefix = logPrefix;
        }
    
        public Class<? extends Log> getLogImpl() {
            return logImpl;
        }
    
        @SuppressWarnings("unchecked")
        public void setLogImpl(Class<?> logImpl) {
            if (logImpl != null) {
                this.logImpl = (Class<? extends Log>) logImpl;
                LogFactory.useCustomLogging(this.logImpl);
            }
        }
    
        public boolean isCallSettersOnNulls() {
            return callSettersOnNulls;
        }
    
        public void setCallSettersOnNulls(boolean callSettersOnNulls) {
            this.callSettersOnNulls = callSettersOnNulls;
        }
    
        public String getDatabaseId() {
            return databaseId;
        }
    
        public void setDatabaseId(String databaseId) {
            this.databaseId = databaseId;
        }
    
        public Class<?> getConfigurationFactory() {
            return configurationFactory;
        }
    
        public void setConfigurationFactory(Class<?> configurationFactory) {
            this.configurationFactory = configurationFactory;
        }
    
        public boolean isSafeResultHandlerEnabled() {
            return safeResultHandlerEnabled;
        }
    
        public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
            this.safeResultHandlerEnabled = safeResultHandlerEnabled;
        }
    
        public boolean isSafeRowBoundsEnabled() {
            return safeRowBoundsEnabled;
        }
    
        public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
            this.safeRowBoundsEnabled = safeRowBoundsEnabled;
        }
    
        public boolean isMapUnderscoreToCamelCase() {
            return mapUnderscoreToCamelCase;
        }
    
        public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
            this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
        }
    
        public void addLoadedResource(String resource) {
            loadedResources.add(resource);
        }
    
        public boolean isResourceLoaded(String resource) {
            return loadedResources.contains(resource);
        }
    
        public Environment getEnvironment() {
            return environment;
        }
    
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
        public AutoMappingBehavior getAutoMappingBehavior() {
            return autoMappingBehavior;
        }
    
        public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
            this.autoMappingBehavior = autoMappingBehavior;
        }
    
        public boolean isLazyLoadingEnabled() {
            return lazyLoadingEnabled;
        }
    
        public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
            this.lazyLoadingEnabled = lazyLoadingEnabled;
        }
    
        public ProxyFactory getProxyFactory() {
            if (proxyFactory == null) {
                // makes sure CGLIB is not needed unless explicitly requested
                proxyFactory = new CglibProxyFactory();
            }
            return proxyFactory;
        }
    
        public void setProxyFactory(ProxyFactory proxyFactory) {
            this.proxyFactory = proxyFactory;
        }
    
        public boolean isAggressiveLazyLoading() {
            return aggressiveLazyLoading;
        }
    
        public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
            this.aggressiveLazyLoading = aggressiveLazyLoading;
        }
    
        public boolean isMultipleResultSetsEnabled() {
            return multipleResultSetsEnabled;
        }
    
        public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
            this.multipleResultSetsEnabled = multipleResultSetsEnabled;
        }
    
        public Set<String> getLazyLoadTriggerMethods() {
            return lazyLoadTriggerMethods;
        }
    
        public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
            this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
        }
    
        public boolean isUseGeneratedKeys() {
            return useGeneratedKeys;
        }
    
        public void setUseGeneratedKeys(boolean useGeneratedKeys) {
            this.useGeneratedKeys = useGeneratedKeys;
        }
    
        public ExecutorType getDefaultExecutorType() {
            return defaultExecutorType;
        }
    
        public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
            this.defaultExecutorType = defaultExecutorType;
        }
    
        public boolean isCacheEnabled() {
            return cacheEnabled;
        }
    
        public void setCacheEnabled(boolean cacheEnabled) {
            this.cacheEnabled = cacheEnabled;
        }
    
        public Integer getDefaultStatementTimeout() {
            return defaultStatementTimeout;
        }
    
        public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
            this.defaultStatementTimeout = defaultStatementTimeout;
        }
    
        public boolean isUseColumnLabel() {
            return useColumnLabel;
        }
    
        public void setUseColumnLabel(boolean useColumnLabel) {
            this.useColumnLabel = useColumnLabel;
        }
    
        public LocalCacheScope getLocalCacheScope() {
            return localCacheScope;
        }
    
        public void setLocalCacheScope(LocalCacheScope localCacheScope) {
            this.localCacheScope = localCacheScope;
        }
    
        public JdbcType getJdbcTypeForNull() {
            return jdbcTypeForNull;
        }
    
        public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
            this.jdbcTypeForNull = jdbcTypeForNull;
        }
    
        public Properties getVariables() {
            return variables;
        }
    
        public void setVariables(Properties variables) {
            this.variables = variables;
        }
    
        public TypeHandlerRegistry getTypeHandlerRegistry() {
            return typeHandlerRegistry;
        }
    
        public TypeAliasRegistry getTypeAliasRegistry() {
            return typeAliasRegistry;
        }
    
        /**
         * @since 3.2.2
         */
        public MapperRegistry getMapperRegistry() {
            return mapperRegistry;
        }
    
        public ObjectFactory getObjectFactory() {
            return objectFactory;
        }
    
        public void setObjectFactory(ObjectFactory objectFactory) {
            this.objectFactory = objectFactory;
        }
    
        public ObjectWrapperFactory getObjectWrapperFactory() {
            return objectWrapperFactory;
        }
    
        public void setObjectWrapperFactory(
                ObjectWrapperFactory objectWrapperFactory) {
            this.objectWrapperFactory = objectWrapperFactory;
        }
    
        /**
         * @since 3.2.2
         */
        public List<Interceptor> getInterceptors() {
            return interceptorChain.getInterceptors();
        }
    
        public LanguageDriverRegistry getLanguageRegistry() {
            return languageRegistry;
        }
    
        public void setDefaultScriptingLanguage(Class<?> driver) {
            if (driver == null) {
                driver = XMLLanguageDriver.class;
            }
            getLanguageRegistry().setDefaultDriverClass(driver);
        }
    
        public LanguageDriver getDefaultScriptingLanuageInstance() {
            return languageRegistry.getDefaultDriver();
        }
    
        public MetaObject newMetaObject(Object object) {
            return MetaObject
                    .forObject(object, objectFactory, objectWrapperFactory);
        }
    
        public ParameterHandler newParameterHandler(
                MappedStatement mappedStatement, Object parameterObject,
                BoundSql boundSql) {
            ParameterHandler parameterHandler = mappedStatement.getLang()
                    .createParameterHandler(mappedStatement, parameterObject,
                            boundSql);
            parameterHandler = (ParameterHandler) interceptorChain
                    .pluginAll(parameterHandler);
            return parameterHandler;
        }
    
        public ResultSetHandler newResultSetHandler(Executor executor,
                MappedStatement mappedStatement, RowBounds rowBounds,
                ParameterHandler parameterHandler, ResultHandler resultHandler,
                BoundSql boundSql) {
            ResultSetHandler resultSetHandler = new DefaultResultSetHandler(
                    executor, mappedStatement, parameterHandler, resultHandler,
                    boundSql, rowBounds);
            resultSetHandler = (ResultSetHandler) interceptorChain
                    .pluginAll(resultSetHandler);
            return resultSetHandler;
        }
    
        public StatementHandler newStatementHandler(Executor executor,
                MappedStatement mappedStatement, Object parameterObject,
                RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
            StatementHandler statementHandler = new RoutingStatementHandler(
                    executor, mappedStatement, parameterObject, rowBounds,
                    resultHandler, boundSql);
            statementHandler = (StatementHandler) interceptorChain
                    .pluginAll(statementHandler);
            return statementHandler;
        }
    
        public Executor newExecutor(Transaction transaction) {
            return newExecutor(transaction, defaultExecutorType);
        }
    
        public Executor newExecutor(Transaction transaction,
                ExecutorType executorType) {
            executorType = executorType == null ? defaultExecutorType
                    : executorType;
            executorType = executorType == null ? ExecutorType.SIMPLE
                    : executorType;
            Executor executor;
            if (ExecutorType.BATCH == executorType) {
                executor = new BatchExecutor(this, transaction);
            } else if (ExecutorType.REUSE == executorType) {
                executor = new ReuseExecutor(this, transaction);
            } else {
                executor = new SimpleExecutor(this, transaction);
            }
            if (cacheEnabled) {
                executor = new CachingExecutor(executor);
            }
            executor = (Executor) interceptorChain.pluginAll(executor);
            return executor;
        }
    
        public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
            keyGenerators.put(id, keyGenerator);
        }
    
        public Collection<String> getKeyGeneratorNames() {
            return keyGenerators.keySet();
        }
    
        public Collection<KeyGenerator> getKeyGenerators() {
            return keyGenerators.values();
        }
    
        public KeyGenerator getKeyGenerator(String id) {
            return keyGenerators.get(id);
        }
    
        public boolean hasKeyGenerator(String id) {
            return keyGenerators.containsKey(id);
        }
    
        public void addCache(Cache cache) {
            caches.put(cache.getId(), cache);
        }
    
        public Collection<String> getCacheNames() {
            return caches.keySet();
        }
    
        public Collection<Cache> getCaches() {
            return caches.values();
        }
    
        public Cache getCache(String id) {
            return caches.get(id);
        }
    
        public boolean hasCache(String id) {
            return caches.containsKey(id);
        }
    
        public void addResultMap(ResultMap rm) {
            resultMaps.put(rm.getId(), rm);
            checkLocallyForDiscriminatedNestedResultMaps(rm);
            checkGloballyForDiscriminatedNestedResultMaps(rm);
        }
    
        public Collection<String> getResultMapNames() {
            return resultMaps.keySet();
        }
    
        public Collection<ResultMap> getResultMaps() {
            return resultMaps.values();
        }
    
        public ResultMap getResultMap(String id) {
            return resultMaps.get(id);
        }
    
        public boolean hasResultMap(String id) {
            return resultMaps.containsKey(id);
        }
    
        public void addParameterMap(ParameterMap pm) {
            parameterMaps.put(pm.getId(), pm);
        }
    
        public Collection<String> getParameterMapNames() {
            return parameterMaps.keySet();
        }
    
        public Collection<ParameterMap> getParameterMaps() {
            return parameterMaps.values();
        }
    
        public ParameterMap getParameterMap(String id) {
            return parameterMaps.get(id);
        }
    
        public boolean hasParameterMap(String id) {
            return parameterMaps.containsKey(id);
        }
    
        public void addMappedStatement(MappedStatement ms) {
            mappedStatements.put(ms.getId(), ms);
        }
    
        public Collection<String> getMappedStatementNames() {
            buildAllStatements();
            return mappedStatements.keySet();
        }
    
        public Collection<MappedStatement> getMappedStatements() {
            buildAllStatements();
            return mappedStatements.values();
        }
    
        public Collection<XMLStatementBuilder> getIncompleteStatements() {
            return incompleteStatements;
        }
    
        public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
            incompleteStatements.add(incompleteStatement);
        }
    
        public Collection<CacheRefResolver> getIncompleteCacheRefs() {
            return incompleteCacheRefs;
        }
    
        public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
            incompleteCacheRefs.add(incompleteCacheRef);
        }
    
        public Collection<ResultMapResolver> getIncompleteResultMaps() {
            return incompleteResultMaps;
        }
    
        public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
            incompleteResultMaps.add(resultMapResolver);
        }
    
        public void addIncompleteMethod(MethodResolver builder) {
            incompleteMethods.add(builder);
        }
    
        public Collection<MethodResolver> getIncompleteMethods() {
            return incompleteMethods;
        }
    
        public MappedStatement getMappedStatement(String id) {
            return this.getMappedStatement(id, true);
        }
    
        public MappedStatement getMappedStatement(String id,
                boolean validateIncompleteStatements) {
            if (validateIncompleteStatements) {
                buildAllStatements();
            }
            return mappedStatements.get(id);
        }
    
        public Map<String, XNode> getSqlFragments() {
            return sqlFragments;
        }
    
        public void addInterceptor(Interceptor interceptor) {
            interceptorChain.addInterceptor(interceptor);
        }
    
        public void addMappers(String packageName, Class<?> superType) {
            mapperRegistry.addMappers(packageName, superType);
        }
    
        public void addMappers(String packageName) {
            mapperRegistry.addMappers(packageName);
        }
    
        public <T> void addMapper(Class<T> type) {
            mapperRegistry.addMapper(type);
        }
    
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return mapperRegistry.getMapper(type, sqlSession);
        }
    
        public boolean hasMapper(Class<?> type) {
            return mapperRegistry.hasMapper(type);
        }
    
        public boolean hasStatement(String statementName) {
            return hasStatement(statementName, true);
        }
    
        public boolean hasStatement(String statementName,
                boolean validateIncompleteStatements) {
            if (validateIncompleteStatements) {
                buildAllStatements();
            }
            return mappedStatements.containsKey(statementName);
        }
    
        public void addCacheRef(String namespace, String referencedNamespace) {
            cacheRefMap.put(namespace, referencedNamespace);
        }
    
        /*
         * Parses all the unprocessed statement nodes in the cache. It is
         * recommended to call this method once all the mappers are added as it
         * provides fail-fast statement validation.
         */
        protected void buildAllStatements() {
            if (!incompleteResultMaps.isEmpty()) {
                synchronized (incompleteResultMaps) {
                    // This always throws a BuilderException.
                    incompleteResultMaps.iterator().next().resolve();
                }
            }
            if (!incompleteCacheRefs.isEmpty()) {
                synchronized (incompleteCacheRefs) {
                    // This always throws a BuilderException.
                    incompleteCacheRefs.iterator().next().resolveCacheRef();
                }
            }
            if (!incompleteStatements.isEmpty()) {
                synchronized (incompleteStatements) {
                    // This always throws a BuilderException.
                    incompleteStatements.iterator().next().parseStatementNode();
                }
            }
            if (!incompleteMethods.isEmpty()) {
                synchronized (incompleteMethods) {
                    // This always throws a BuilderException.
                    incompleteMethods.iterator().next().resolve();
                }
            }
        }
    
        /*
         * Extracts namespace from fully qualified statement id.
         * 
         * @param statementId
         * 
         * @return namespace or null when id does not contain period.
         */
        protected String extractNamespace(String statementId) {
            int lastPeriod = statementId.lastIndexOf('.');
            return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
        }
    
        // Slow but a one time cost. A better solution is welcome.
        protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
            if (rm.hasNestedResultMaps()) {
                for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
                    Object value = entry.getValue();
                    if (value instanceof ResultMap) {
                        ResultMap entryResultMap = (ResultMap) value;
                        if (!entryResultMap.hasNestedResultMaps()
                                && entryResultMap.getDiscriminator() != null) {
                            Collection<String> discriminatedResultMapNames = entryResultMap
                                    .getDiscriminator().getDiscriminatorMap()
                                    .values();
                            if (discriminatedResultMapNames.contains(rm.getId())) {
                                entryResultMap.forceNestedResultMaps();
                            }
                        }
                    }
                }
            }
        }
    
        // Slow but a one time cost. A better solution is welcome.
        protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
            if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
                for (Map.Entry<String, String> entry : rm.getDiscriminator()
                        .getDiscriminatorMap().entrySet()) {
                    String discriminatedResultMapName = entry.getValue();
                    if (hasResultMap(discriminatedResultMapName)) {
                        ResultMap discriminatedResultMap = resultMaps
                                .get(discriminatedResultMapName);
                        if (discriminatedResultMap.hasNestedResultMaps()) {
                            rm.forceNestedResultMaps();
                            break;
                        }
                    }
                }
            }
        }
      
        protected static class StrictMap<V> extends HashMap<String, V> {
    
            private static final long serialVersionUID = -4950446264854982944L;
            private String name;
    
            public StrictMap(String name, int initialCapacity, float loadFactor) {
                super(initialCapacity, loadFactor);
                this.name = name;
            }
    
            public StrictMap(String name, int initialCapacity) {
                super(initialCapacity);
                this.name = name;
            }
    
            public StrictMap(String name) {
                super();
                this.name = name;
            }
    
            public StrictMap(String name, Map<String, ? extends V> m) {
                super(m);
                this.name = name;
            }
    
            /**
             * 如果现在状态为刷新,则刷新(先删除后添加)
             */
            @SuppressWarnings("unchecked")
            public V put(String key, V value) {
                if (org.apache.ibatis.thread.RefreshRunnable.isRefresh()) {
                    remove(key);
                    org.apache.ibatis.thread.RefreshRunnable.log.debug("refresh key:"
                            + key.substring(key.lastIndexOf(".") + 1));
                }
                if (containsKey(key))
                    throw new IllegalArgumentException(name
                            + " already contains value for " + key);
                if (key.contains(".")) {
                    final String shortKey = getShortName(key);
                    if (super.get(shortKey) == null) {
                        super.put(shortKey, value);
                    } else {
                        super.put(shortKey, (V) new Ambiguity(shortKey));
                    }
                }
                return super.put(key, value);
            }
    
            public V get(Object key) {
                V value = super.get(key);
                if (value == null) {
                    throw new IllegalArgumentException(name
                            + " does not contain value for " + key);
                }
                if (value instanceof Ambiguity) {
                    throw new IllegalArgumentException(
                            ((Ambiguity) value).getSubject()
                                    + " is ambiguous in "
                                    + name
                                    + " (try using the full name including the namespace, or rename one of the entries)");
                }
                return value;
            }
    
            private String getShortName(String key) {
                final String[] keyparts = key.split("\.");
                final String shortKey = keyparts[keyparts.length - 1];
                return shortKey;
            }
    
            protected static class Ambiguity {
                private String subject;
    
                public Ambiguity(String subject) {
                    this.subject = subject;
                }
    
                public String getSubject() {
                    return subject;
                }
            }
        }
    
    }
    /*
     *    Copyright 2009-2013 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.builder.xml;
    
    import java.io.InputStream;
    import java.io.Reader;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    
    import org.apache.ibatis.builder.BaseBuilder;
    import org.apache.ibatis.builder.BuilderException;
    import org.apache.ibatis.builder.CacheRefResolver;
    import org.apache.ibatis.builder.IncompleteElementException;
    import org.apache.ibatis.builder.MapperBuilderAssistant;
    import org.apache.ibatis.builder.ResultMapResolver;
    import org.apache.ibatis.cache.Cache;
    import org.apache.ibatis.executor.ErrorContext;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.mapping.Discriminator;
    import org.apache.ibatis.mapping.ParameterMapping;
    import org.apache.ibatis.mapping.ParameterMode;
    import org.apache.ibatis.mapping.ResultFlag;
    import org.apache.ibatis.mapping.ResultMap;
    import org.apache.ibatis.mapping.ResultMapping;
    import org.apache.ibatis.parsing.XNode;
    import org.apache.ibatis.parsing.XPathParser;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.TypeHandler;
    
    /**
     * @author Clinton Begin
     * @description 增加解析方法,原方法不解析相同的resource
     */
    public class XMLMapperBuilder extends BaseBuilder {
    
        private XPathParser parser;
        private MapperBuilderAssistant builderAssistant;
        private Map<String, XNode> sqlFragments;
        private String resource;
    
        @Deprecated
        public XMLMapperBuilder(Reader reader, Configuration configuration,
                String resource, Map<String, XNode> sqlFragments, String namespace) {
            this(reader, configuration, resource, sqlFragments);
            this.builderAssistant.setCurrentNamespace(namespace);
        }
    
        @Deprecated
        public XMLMapperBuilder(Reader reader, Configuration configuration,
                String resource, Map<String, XNode> sqlFragments) {
            this(new XPathParser(reader, true, configuration.getVariables(),
                    new XMLMapperEntityResolver()), configuration, resource,
                    sqlFragments);
        }
    
        public XMLMapperBuilder(InputStream inputStream,
                Configuration configuration, String resource,
                Map<String, XNode> sqlFragments, String namespace) {
            this(inputStream, configuration, resource, sqlFragments);
            this.builderAssistant.setCurrentNamespace(namespace);
        }
    
        public XMLMapperBuilder(InputStream inputStream,
                Configuration configuration, String resource,
                Map<String, XNode> sqlFragments) {
            this(new XPathParser(inputStream, true, configuration.getVariables(),
                    new XMLMapperEntityResolver()), configuration, resource,
                    sqlFragments);
        }
    
        private XMLMapperBuilder(XPathParser parser, Configuration configuration,
                String resource, Map<String, XNode> sqlFragments) {
            super(configuration);
            this.builderAssistant = new MapperBuilderAssistant(configuration,
                    resource);
            this.parser = parser;
            this.sqlFragments = sqlFragments;
            this.resource = resource;
        }
    
        public void parse() {
            if (!configuration.isResourceLoaded(resource)) {
                configurationElement(parser.evalNode("/mapper"));
                configuration.addLoadedResource(resource);
                bindMapperForNamespace();
            }
    
            parsePendingResultMaps();
            parsePendingChacheRefs();
            parsePendingStatements();
        }
    
        public void parse1() {
            // if (!configuration.isResourceLoaded(resource)) {
            configurationElement(parser.evalNode("/mapper"));
            configuration.addLoadedResource(resource);
            bindMapperForNamespace();
            // }
    
            parsePendingResultMaps();
            parsePendingChacheRefs();
            parsePendingStatements();
        }
    
        public XNode getSqlFragment(String refid) {
            return sqlFragments.get(refid);
        }
    
        private void configurationElement(XNode context) {
            try {
                String namespace = context.getStringAttribute("namespace");
                if (namespace.equals("")) {
                    throw new BuilderException("Mapper's namespace cannot be empty");
                }
                builderAssistant.setCurrentNamespace(namespace);
                cacheRefElement(context.evalNode("cache-ref"));
                cacheElement(context.evalNode("cache"));
                parameterMapElement(context.evalNodes("/mapper/parameterMap"));
                resultMapElements(context.evalNodes("/mapper/resultMap"));
                sqlElement(context.evalNodes("/mapper/sql"));
                buildStatementFromContext(context
                        .evalNodes("select|insert|update|delete"));
            } catch (Exception e) {
                throw new BuilderException("Error parsing Mapper XML. Cause: " + e,
                        e);
            }
        }
    
        private void buildStatementFromContext(List<XNode> list) {
            if (configuration.getDatabaseId() != null) {
                buildStatementFromContext(list, configuration.getDatabaseId());
            }
            buildStatementFromContext(list, null);
        }
    
        private void buildStatementFromContext(List<XNode> list,
                String requiredDatabaseId) {
            for (XNode context : list) {
                final XMLStatementBuilder statementParser = new XMLStatementBuilder(
                        configuration, builderAssistant, context,
                        requiredDatabaseId);
                try {
                    statementParser.parseStatementNode();
                } catch (IncompleteElementException e) {
                    configuration.addIncompleteStatement(statementParser);
                }
            }
        }
    
        private void parsePendingResultMaps() {
            Collection<ResultMapResolver> incompleteResultMaps = configuration
                    .getIncompleteResultMaps();
            synchronized (incompleteResultMaps) {
                Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
                while (iter.hasNext()) {
                    try {
                        iter.next().resolve();
                        iter.remove();
                    } catch (IncompleteElementException e) {
                        // ResultMap is still missing a resource...
                    }
                }
            }
        }
    
        private void parsePendingChacheRefs() {
            Collection<CacheRefResolver> incompleteCacheRefs = configuration
                    .getIncompleteCacheRefs();
            synchronized (incompleteCacheRefs) {
                Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
                while (iter.hasNext()) {
                    try {
                        iter.next().resolveCacheRef();
                        iter.remove();
                    } catch (IncompleteElementException e) {
                        // Cache ref is still missing a resource...
                    }
                }
            }
        }
    
        private void parsePendingStatements() {
            Collection<XMLStatementBuilder> incompleteStatements = configuration
                    .getIncompleteStatements();
            synchronized (incompleteStatements) {
                Iterator<XMLStatementBuilder> iter = incompleteStatements
                        .iterator();
                while (iter.hasNext()) {
                    try {
                        iter.next().parseStatementNode();
                        iter.remove();
                    } catch (IncompleteElementException e) {
                        // Statement is still missing a resource...
                    }
                }
            }
        }
    
        private void cacheRefElement(XNode context) {
            if (context != null) {
                configuration.addCacheRef(builderAssistant.getCurrentNamespace(),
                        context.getStringAttribute("namespace"));
                CacheRefResolver cacheRefResolver = new CacheRefResolver(
                        builderAssistant, context.getStringAttribute("namespace"));
                try {
                    cacheRefResolver.resolveCacheRef();
                } catch (IncompleteElementException e) {
                    configuration.addIncompleteCacheRef(cacheRefResolver);
                }
            }
        }
    
        private void cacheElement(XNode context) throws Exception {
            if (context != null) {
                String type = context.getStringAttribute("type", "PERPETUAL");
                Class<? extends Cache> typeClass = typeAliasRegistry
                        .resolveAlias(type);
                String eviction = context.getStringAttribute("eviction", "LRU");
                Class<? extends Cache> evictionClass = typeAliasRegistry
                        .resolveAlias(eviction);
                Long flushInterval = context.getLongAttribute("flushInterval");
                Integer size = context.getIntAttribute("size");
                boolean readWrite = !context.getBooleanAttribute("readOnly", false);
                Properties props = context.getChildrenAsProperties();
                builderAssistant.useNewCache(typeClass, evictionClass,
                        flushInterval, size, readWrite, props);
            }
        }
    
        private void parameterMapElement(List<XNode> list) throws Exception {
            for (XNode parameterMapNode : list) {
                String id = parameterMapNode.getStringAttribute("id");
                String type = parameterMapNode.getStringAttribute("type");
                Class<?> parameterClass = resolveClass(type);
                List<XNode> parameterNodes = parameterMapNode
                        .evalNodes("parameter");
                List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
                for (XNode parameterNode : parameterNodes) {
                    String property = parameterNode.getStringAttribute("property");
                    String javaType = parameterNode.getStringAttribute("javaType");
                    String jdbcType = parameterNode.getStringAttribute("jdbcType");
                    String resultMap = parameterNode
                            .getStringAttribute("resultMap");
                    String mode = parameterNode.getStringAttribute("mode");
                    String typeHandler = parameterNode
                            .getStringAttribute("typeHandler");
                    Integer numericScale = parameterNode
                            .getIntAttribute("numericScale");
                    ParameterMode modeEnum = resolveParameterMode(mode);
                    Class<?> javaTypeClass = resolveClass(javaType);
                    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
                    @SuppressWarnings("unchecked")
                    Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
                    ParameterMapping parameterMapping = builderAssistant
                            .buildParameterMapping(parameterClass, property,
                                    javaTypeClass, jdbcTypeEnum, resultMap,
                                    modeEnum, typeHandlerClass, numericScale);
                    parameterMappings.add(parameterMapping);
                }
                builderAssistant.addParameterMap(id, parameterClass,
                        parameterMappings);
            }
        }
    
        private void resultMapElements(List<XNode> list) throws Exception {
            for (XNode resultMapNode : list) {
                try {
                    resultMapElement(resultMapNode);
                } catch (IncompleteElementException e) {
                    // ignore, it will be retried
                }
            }
        }
    
        private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
            return resultMapElement(resultMapNode,
                    Collections.<ResultMapping> emptyList());
        }
    
        private ResultMap resultMapElement(XNode resultMapNode,
                List<ResultMapping> additionalResultMappings) throws Exception {
            ErrorContext.instance().activity(
                    "processing " + resultMapNode.getValueBasedIdentifier());
            String id = resultMapNode.getStringAttribute("id",
                    resultMapNode.getValueBasedIdentifier());
            String type = resultMapNode.getStringAttribute("type", resultMapNode
                    .getStringAttribute("ofType", resultMapNode.getStringAttribute(
                            "resultType",
                            resultMapNode.getStringAttribute("javaType"))));
            String extend = resultMapNode.getStringAttribute("extends");
            Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
            Class<?> typeClass = resolveClass(type);
            Discriminator discriminator = null;
            List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
            resultMappings.addAll(additionalResultMappings);
            List<XNode> resultChildren = resultMapNode.getChildren();
            for (XNode resultChild : resultChildren) {
                if ("constructor".equals(resultChild.getName())) {
                    processConstructorElement(resultChild, typeClass,
                            resultMappings);
                } else if ("discriminator".equals(resultChild.getName())) {
                    discriminator = processDiscriminatorElement(resultChild,
                            typeClass, resultMappings);
                } else {
                    ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
                    if ("id".equals(resultChild.getName())) {
                        flags.add(ResultFlag.ID);
                    }
                    resultMappings.add(buildResultMappingFromContext(resultChild,
                            typeClass, flags));
                }
            }
            ResultMapResolver resultMapResolver = new ResultMapResolver(
                    builderAssistant, id, typeClass, extend, discriminator,
                    resultMappings, autoMapping);
            try {
                return resultMapResolver.resolve();
            } catch (IncompleteElementException e) {
                configuration.addIncompleteResultMap(resultMapResolver);
                throw e;
            }
        }
    
        private void processConstructorElement(XNode resultChild,
                Class<?> resultType, List<ResultMapping> resultMappings)
                throws Exception {
            List<XNode> argChildren = resultChild.getChildren();
            for (XNode argChild : argChildren) {
                ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
                flags.add(ResultFlag.CONSTRUCTOR);
                if ("idArg".equals(argChild.getName())) {
                    flags.add(ResultFlag.ID);
                }
                resultMappings.add(buildResultMappingFromContext(argChild,
                        resultType, flags));
            }
        }
    
        private Discriminator processDiscriminatorElement(XNode context,
                Class<?> resultType, List<ResultMapping> resultMappings)
                throws Exception {
            String column = context.getStringAttribute("column");
            String javaType = context.getStringAttribute("javaType");
            String jdbcType = context.getStringAttribute("jdbcType");
            String typeHandler = context.getStringAttribute("typeHandler");
            Class<?> javaTypeClass = resolveClass(javaType);
            @SuppressWarnings("unchecked")
            Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
            JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
            Map<String, String> discriminatorMap = new HashMap<String, String>();
            for (XNode caseChild : context.getChildren()) {
                String value = caseChild.getStringAttribute("value");
                String resultMap = caseChild.getStringAttribute("resultMap",
                        processNestedResultMappings(caseChild, resultMappings));
                discriminatorMap.put(value, resultMap);
            }
            return builderAssistant
                    .buildDiscriminator(resultType, column, javaTypeClass,
                            jdbcTypeEnum, typeHandlerClass, discriminatorMap);
        }
    
        private void sqlElement(List<XNode> list) throws Exception {
            if (configuration.getDatabaseId() != null) {
                sqlElement(list, configuration.getDatabaseId());
            }
            sqlElement(list, null);
        }
    
        private void sqlElement(List<XNode> list, String requiredDatabaseId)
                throws Exception {
            for (XNode context : list) {
                String databaseId = context.getStringAttribute("databaseId");
                String id = context.getStringAttribute("id");
                id = builderAssistant.applyCurrentNamespace(id, false);
                if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId))
                    sqlFragments.put(id, context);
            }
        }
    
        private boolean databaseIdMatchesCurrent(String id, String databaseId,
                String requiredDatabaseId) {
            if (requiredDatabaseId != null) {
                if (!requiredDatabaseId.equals(databaseId)) {
                    return false;
                }
            } else {
                if (databaseId != null) {
                    return false;
                }
                // skip this fragment if there is a previous one with a not null
                // databaseId
                if (this.sqlFragments.containsKey(id)) {
                    XNode context = this.sqlFragments.get(id);
                    if (context.getStringAttribute("databaseId") != null) {
                        return false;
                    }
                }
            }
            return true;
        }
    
        private ResultMapping buildResultMappingFromContext(XNode context,
                Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
            String property = context.getStringAttribute("property");
            String column = context.getStringAttribute("column");
            String javaType = context.getStringAttribute("javaType");
            String jdbcType = context.getStringAttribute("jdbcType");
            String nestedSelect = context.getStringAttribute("select");
            String nestedResultMap = context.getStringAttribute(
                    "resultMap",
                    processNestedResultMappings(context,
                            Collections.<ResultMapping> emptyList()));
            String notNullColumn = context.getStringAttribute("notNullColumn");
            String columnPrefix = context.getStringAttribute("columnPrefix");
            String typeHandler = context.getStringAttribute("typeHandler");
            String resulSet = context.getStringAttribute("resultSet");
            String foreignColumn = context.getStringAttribute("foreignColumn");
            boolean lazy = "lazy".equals(context.getStringAttribute("fetchType",
                    configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
            Class<?> javaTypeClass = resolveClass(javaType);
            @SuppressWarnings("unchecked")
            Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
            JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
            return builderAssistant.buildResultMapping(resultType, property,
                    column, javaTypeClass, jdbcTypeEnum, nestedSelect,
                    nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass,
                    flags, resulSet, foreignColumn, lazy);
        }
    
        private String processNestedResultMappings(XNode context,
                List<ResultMapping> resultMappings) throws Exception {
            if ("association".equals(context.getName())
                    || "collection".equals(context.getName())
                    || "case".equals(context.getName())) {
                if (context.getStringAttribute("select") == null) {
                    ResultMap resultMap = resultMapElement(context, resultMappings);
                    return resultMap.getId();
                }
            }
            return null;
        }
    
        private void bindMapperForNamespace() {
            String namespace = builderAssistant.getCurrentNamespace();
            if (namespace != null) {
                Class<?> boundType = null;
                try {
                    boundType = Resources.classForName(namespace);
                } catch (ClassNotFoundException e) {
                    // ignore, bound type is not required
                }
                if (boundType != null) {
                    if (!configuration.hasMapper(boundType)) {
                        // Spring may not know the real resource name so we set a
                        // flag
                        // to prevent loading again this resource from the mapper
                        // interface
                        // look at MapperAnnotationBuilder#loadXmlResource
                        configuration.addLoadedResource("namespace:" + namespace);
                        configuration.addMapper(boundType);
                    }
                }
            }
        }
    
    }

    增加刷新xml的线程(核心配置):

    package org.apache.ibatis.thread;
    
    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    import org.apache.ibatis.session.Configuration;
    import org.apache.log4j.Logger;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.springframework.core.NestedIOException;
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.core.io.Resource;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 刷新使用进程
     *
     * @author gongtao
     * @date 2019-04-09
     */
    public class RefreshRunnable implements java.lang.Runnable {
    
        public static Logger log = Logger.getLogger(RefreshRunnable.class);
    
        private Resource[] mapperLocations;
        private Configuration configuration;
    
        /**
         * 上一次刷新时间
         */
        private Long beforeTime = 0L;
        /**
         * 是否执行刷新
         */
        private static boolean refresh = false;
        /**
         * 延迟刷新秒数
         */
        private static int delaySeconds;
        /**
         * 休眠时间
         */
        private static int sleepSeconds;
        /**
         * 是否启用
         */
        private static boolean enabled;
    
        /**
         * 是否关闭
         */
        private static boolean closed = false;
    
        /** 线程工厂 */
        private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setNameFormat("mybatis-xml-reload-%d").build();
        /** 定时扫描线程池 */
        private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1, THREAD_FACTORY);
    
        static {
            delaySeconds = PropertiesUtil.getInt("delaySeconds");
            sleepSeconds = PropertiesUtil.getInt("sleepSeconds");
            enabled = "true".equals(PropertiesUtil.getString("enabled"));
    
            delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;
            sleepSeconds = sleepSeconds == 0 ? 1 : sleepSeconds;
    
            log.debug("[delaySeconds] " + delaySeconds);
            log.debug("[sleepSeconds] " + sleepSeconds);
    
        }
    
        public static boolean isRefresh() {
            return refresh;
        }
    
        public RefreshRunnable(Resource[] mapperLocations, Configuration configuration) {
            this.mapperLocations = mapperLocations;
            this.configuration = configuration;
        }
    
        @Override
        public void run() {
    
            beforeTime = System.currentTimeMillis();
            if (enabled) {
                start(this);
            }
        }
    
        public void start(final RefreshRunnable runnable) {
            refresh = true;
            System.out.println("Enable refresh mybatis xml thread...");
            EXECUTOR.scheduleAtFixedRate(() -> {
                try {
                    runnable.refresh(mapperLocations, beforeTime);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, delaySeconds, sleepSeconds, TimeUnit.SECONDS);
        }
    
        /**
         * 关闭热加载线程
         */
        public static void closeReloadThread(){
            log.info("Shutting down mybatis-xml-reload scheduled job");
            if (enabled && refresh && !closed){
                EXECUTOR.shutdown();
                closed = true;
            }
        }
    
        /**
         * 执行刷新
         *
         * @param mapperLocations 刷新目录
         * @param beforeTime      上次刷新时间
         * @throws NestedIOException     解析异常
         * @throws FileNotFoundException 文件未找到
         */
        public void refresh(Resource[] mapperLocations, Long beforeTime) throws Exception {
    
            // 本次刷新时间
            Long refreshTime = System.currentTimeMillis();
    
            List<File> refreshList = this.getRefreshFile(mapperLocations,
                    beforeTime);
            if (refreshList.size() > 0) {
                log.debug("refresh files:" + refreshList.size());
            }
            for (File file : refreshList) {
                System.out.println("Refresh file: " + file.getAbsolutePath());
                log.debug("refresh file:" + file.getAbsolutePath());
                log.debug("refresh filename:" + file.getName());
    
                // 清理已加载的资源标识,方便让它重新加载。
                Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");
                loadedResourcesField.setAccessible(true);
                Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));
                loadedResourcesSet.remove(file.getAbsolutePath());
    
                SqlSessionFactoryBean.refresh(new FileInputStream(file),
                        file.getAbsolutePath(), configuration);
            }
            // 如果刷新了文件,则修改刷新时间,否则不修改
            if (refreshList.size() > 0) {
                this.beforeTime = refreshTime;
            }
        }
    
        /**
         * 获取需要刷新的文件列表
         *
         * @param mapperLocations 目录
         * @param beforeTime      上次刷新时间
         * @return 刷新文件列表
         */
        private List<File> getRefreshFile(Resource[] mapperLocations, Long beforeTime) {
            List<File> refreshList = new ArrayList<>();
            for (Resource mapperLocation : mapperLocations) {
                try {
                    if (mapperLocation instanceof FileSystemResource) {
                        File file = mapperLocation.getFile();
                        if (this.check(file, beforeTime)) {
                            refreshList.add(file);
                        }
                    }
                } catch (IOException e) {
                    log.error("get file error", e);
                }
            }
    
            return refreshList;
        }
    
        /**
         * 判断文件是否需要刷新
         *
         * @param file       文件
         * @param beforeTime 上次刷新时间
         * @return 需要刷新返回true,否则返回false
         */
        public boolean check(File file, Long beforeTime) {
            return file.lastModified() > beforeTime;
        }
    
    
    }

    增加刷新xml的配置文件及工具类:

    package org.apache.ibatis.thread;
    
    import java.util.Objects;
    import java.util.Properties;
    
    public class PropertiesUtil {
    
        private static String filename = "/mybatis-refresh.properties";
        private static Properties pro = new Properties();
        static {
            try {
                pro.load(PropertiesUtil.class.getResourceAsStream(filename));
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Load mybatis-refresh “"+filename+"” file error.");
            }
        }
    
        public static int getInt(String key) {
            int i = 0;
            try {
                i = Integer.parseInt(Objects.requireNonNull(getString(key)));
            } catch (Exception e) {
                //ignore
            }
            return i;
        }
    
        public static String getString(String key) {
            return pro == null ? null : pro.getProperty(key);
        }
    
    }

    配置文件mybatis-refresh.xml:

    # enabled
    enabled=true
    # project start time[项目启动时间]
    delaySeconds=5
    # xml scanning interval time[扫描间隔时长]
    sleepSeconds=2

    这样就OK了,但是还有个问题,就是在关闭spring容器时,由于刷新线程一直存在,导致不能完全优雅关闭容器,故增加关闭刷新线程操作:

    package com.xx.common.listener;
    
    import org.apache.ibatis.thread.RefreshRunnable;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.stereotype.Component;
    
    /**
     * spring 关闭时监听器
     * @author gongtao
     * @date 2019-04-11 9:37
     **/
    @Component
    public class ContainerClosedListener implements ApplicationListener<ContextClosedEvent> {
        
        @Override
        public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
            //关闭mybatis 热加载线程
            RefreshRunnable.closeReloadThread();
        }
    }

    如此,大功告成!



  • 相关阅读:
    栈(代码分解)
    线性表(代码分解)
    绪论简概
    1006 Sign In and Sign Out (25 分)
    1005 Spell It Right (20 分)
    分支界限法解决0/1背包问题
    Sequence( 分块+矩阵快速幂 )
    Shape Number (最小表示法)
    Age of Moyu (2018 Multi-University Training Contest 7)
    [Cqoi2014]危桥 (两遍网络流)
  • 原文地址:https://www.cnblogs.com/suiyueqiannian/p/10785480.html
Copyright © 2020-2023  润新知