mybatis 热部署xml文件(spring boot和springmvc两种方式)

参考:http://thinkgem.iteye.com/blog/2304557

步骤:1、创建两个java类

  (1)MapperRefresh.java   :用于刷新mapper

  (2)SqlSessionFactoryBean.java :加入启动上面写的线程类,修复一些mybatis的缺陷

  (3)配置xml文件 (springmvc )或SpringBoot配置

 第一步:MapperRefresh.java 

/** 
 * Copyright (c) 2012-Now https://github.com/thinkgem/jeesite. 
 */  
package com.thinkgem.jeesite.mybatis.thread;  
  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.InputStream;  
import java.lang.reflect.Field;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.Properties;  
import java.util.Set;  
  
import org.apache.commons.lang3.StringUtils;  
import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
import org.apache.ibatis.executor.ErrorContext;  
import org.apache.ibatis.session.Configuration;  
import org.apache.log4j.Logger;  
import org.springframework.core.NestedIOException;  
import org.springframework.core.io.Resource;  
  
import com.google.common.collect.Sets;  
  
/** 
 * 刷新MyBatis Mapper XML 线程 
 * @author ThinkGem 
 * @version 2016-5-29 
 */  
public class MapperRefresh implements java.lang.Runnable {  
  
    public static Logger log = Logger.getLogger(MapperRefresh.class);  
  
    private static String filename = "/mybatis-refresh.properties";  
    private static Properties prop = new Properties();  
  
    private static boolean enabled;         // 是否启用Mapper刷新线程功能  
    private static boolean refresh;         // 刷新启用后,是否启动了刷新线程  
      
    private Set<String> location;         // Mapper实际资源路径  
      
    private Resource[] mapperLocations;     // Mapper资源路径  
    private Configuration configuration;        // MyBatis配置对象  
      
    private Long beforeTime = 0L;           // 上一次刷新时间  
    private static int delaySeconds;        // 延迟刷新秒数  
    private static int sleepSeconds;        // 休眠时间  
    private static String mappingPath;      // xml文件夹匹配字符串,需要根据需要修改  
  
    static {  
          
        try {  
            prop.load(MapperRefresh.class.getResourceAsStream(filename));  
        } catch (Exception e) {  
            e.printStackTrace();  
            System.out.println("Load mybatis-refresh “"+filename+"” file error.");  
        }  
  
        enabled = "true".equalsIgnoreCase(getPropString("enabled"));  
          
        delaySeconds = getPropInt("delaySeconds");  
        sleepSeconds = getPropInt("sleepSeconds");  
        mappingPath = getPropString("mappingPath");  
  
        delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;  
        sleepSeconds = sleepSeconds == 0 ? 3 : sleepSeconds;  
        mappingPath = StringUtils.isBlank(mappingPath) ? "mappings" : mappingPath;  
  
        log.debug("[enabled] " + enabled);  
        log.debug("[delaySeconds] " + delaySeconds);  
        log.debug("[sleepSeconds] " + sleepSeconds);  
        log.debug("[mappingPath] " + mappingPath);  
    }  
  
    public static boolean isRefresh() {  
        return refresh;  
    }  
  
    public MapperRefresh(Resource[] mapperLocations, Configuration configuration) {  
        this.mapperLocations = mapperLocations;  
        this.configuration = configuration;  
    }  
  
    @Override  
    public void run() {  
  
        beforeTime = System.currentTimeMillis();  
  
        log.debug("[location] " + location);  
        log.debug("[configuration] " + configuration);  
  
        if (enabled) {  
            // 启动刷新线程  
            final MapperRefresh runnable = this;  
            new Thread(new java.lang.Runnable() {  
                @Override  
                public void run() {  
                      
                    if (location == null){  
                        location = Sets.newHashSet();  
                        log.debug("MapperLocation's length:" + mapperLocations.length);  
                        for (Resource mapperLocation : mapperLocations) {  
                            String s = mapperLocation.toString().replaceAll("\\", "/");  
                            s = s.substring("file [".length(), s.lastIndexOf(mappingPath) + mappingPath.length());  
                            if (!location.contains(s)) {  
                                location.add(s);  
                                log.debug("Location:" + s);  
                            }  
                        }  
                        log.debug("Locarion's size:" + location.size());  
                    }  
  
                    try {  
                        Thread.sleep(delaySeconds * 1000);  
                    } catch (InterruptedException e2) {  
                        e2.printStackTrace();  
                    }  
                    refresh = true;  
  
                    System.out.println("========= Enabled refresh mybatis mapper =========");  
  
                    while (true) {  
                        try {  
                            for (String s : location) {  
                                runnable.refresh(s, beforeTime);  
                            }  
                        } catch (Exception e1) {  
                            e1.printStackTrace();  
                        }  
                        try {  
                            Thread.sleep(sleepSeconds * 1000);  
                        } catch (InterruptedException e) {  
                            e.printStackTrace();  
                        }  
  
                    }  
                }  
            }, "MyBatis-Mapper-Refresh").start();  
        }  
    }  
  
    /** 
     * 执行刷新 
     * @param filePath 刷新目录 
     * @param beforeTime 上次刷新时间 
     * @throws NestedIOException 解析异常 
     * @throws FileNotFoundException 文件未找到 
     * @author ThinkGem 
     */  
    @SuppressWarnings({ "rawtypes", "unchecked" })  
    private void refresh(String filePath, Long beforeTime) throws Exception {  
  
        // 本次刷新时间  
        Long refrehTime = System.currentTimeMillis();  
  
        // 获取需要刷新的Mapper文件列表  
        List<File> fileList = this.getRefreshFile(new File(filePath), beforeTime);  
        if (fileList.size() > 0) {  
            log.debug("Refresh file: " + fileList.size());  
        }  
        for (int i = 0; i < fileList.size(); i++) {  
            InputStream inputStream = new FileInputStream(fileList.get(i));  
            String resource = fileList.get(i).getAbsolutePath();  
            try {  
                  
                // 清理原有资源,更新为自己的StrictMap方便,增量重新加载  
                String[] mapFieldNames = new String[]{  
                    "mappedStatements", "caches",  
                    "resultMaps", "parameterMaps",  
                    "keyGenerators", "sqlFragments"  
                };  
                for (String fieldName : mapFieldNames){  
                    Field field = configuration.getClass().getDeclaredField(fieldName);  
                    field.setAccessible(true);  
                    Map map = ((Map)field.get(configuration));  
                    if (!(map instanceof StrictMap)){  
                        Map newMap = new StrictMap(StringUtils.capitalize(fieldName) + "collection");  
                        for (Object key : map.keySet()){  
                            try {  
                                newMap.put(key, map.get(key));  
                            }catch(IllegalArgumentException ex){  
                                newMap.put(key, ex.getMessage());  
                            }  
                        }  
                        field.set(configuration, newMap);  
                    }  
                }  
                  
                // 清理已加载的资源标识,方便让它重新加载。  
                Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");  
                loadedResourcesField.setAccessible(true);  
                Set loadedResourcesSet = ((Set)loadedResourcesField.get(configuration));  
                loadedResourcesSet.remove(resource);  
                  
                //重新编译加载资源文件。  
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration,   
                        resource, configuration.getSqlFragments());  
                xmlMapperBuilder.parse();  
            } catch (Exception e) {  
                throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);  
            } finally {  
                ErrorContext.instance().reset();  
            }  
            System.out.println("Refresh file: " + mappingPath + StringUtils.substringAfterLast(fileList.get(i).getAbsolutePath(), mappingPath));  
            if (log.isDebugEnabled()) {  
                log.debug("Refresh file: " + fileList.get(i).getAbsolutePath());  
                log.debug("Refresh filename: " + fileList.get(i).getName());  
            }  
        }  
        // 如果刷新了文件,则修改刷新时间,否则不修改  
        if (fileList.size() > 0) {  
            this.beforeTime = refrehTime;  
        }  
    }  
      
    /** 
     * 获取需要刷新的文件列表 
     * @param dir 目录 
     * @param beforeTime 上次刷新时间 
     * @return 刷新文件列表 
     */  
    private List<File> getRefreshFile(File dir, Long beforeTime) {  
        List<File> fileList = new ArrayList<File>();  
  
        File[] files = dir.listFiles();  
        if (files != null) {  
            for (int i = 0; i < files.length; i++) {  
                File file = files[i];  
                if (file.isDirectory()) {  
                    fileList.addAll(this.getRefreshFile(file, beforeTime));  
                } else if (file.isFile()) {  
                    if (this.checkFile(file, beforeTime)) {  
                        fileList.add(file);  
                    }  
                } else {  
                    System.out.println("Error file." + file.getName());  
                }  
            }  
        }  
        return fileList;  
    }  
  
    /** 
     * 判断文件是否需要刷新 
     * @param file 文件 
     * @param beforeTime 上次刷新时间 
     * @return 需要刷新返回true,否则返回false 
     */  
    private boolean checkFile(File file, Long beforeTime) {  
        if (file.lastModified() > beforeTime) {  
            return true;  
        }  
        return false;  
    }  
  
    /** 
     * 获取整数属性 
     * @param key 
     * @return 
     */  
    private static int getPropInt(String key) {  
        int i = 0;  
        try {  
            i = Integer.parseInt(getPropString(key));  
        } catch (Exception e) {  
        }  
        return i;  
    }  
  
    /** 
     * 获取字符串属性 
     * @param key 
     * @return 
     */  
    private static String getPropString(String key) {  
        return prop == null ? null : prop.getProperty(key);  
    }  
  
    /** 
     * 重写 org.apache.ibatis.session.Configuration.StrictMap 类 
     * 来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。 
     */  
    public 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) {  
            // ThinkGem 如果现在状态为刷新,则刷新(先删除后添加)  
            if (MapperRefresh.isRefresh()) {  
                remove(key);  
                MapperRefresh.log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + 1));  
            }  
            // ThinkGem end  
            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("\.");  
            return keyparts[keyparts.length - 1];  
        }  
  
        protected static class Ambiguity {  
            private String subject;  
  
            public Ambiguity(String subject) {  
                this.subject = subject;  
            }  
  
            public String getSubject() {  
                return subject;  
            }  
        }  
    }  
}  

第二步:SqlSessionFactoryBean.java   

/** 
 *    Copyright 2010-2015 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 com.thinkgem.jeesite.mybatis.spring;  
  
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;  
  
import java.io.IOException;  
import java.sql.SQLException;  
import java.util.Properties;  
  
import javax.sql.DataSource;  
  
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.Resource;  
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;  
  
import com.thinkgem.jeesite.common.mybatis.thread.MapperRefresh;  
  
/** 
 * {@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$ 
 * @modify ThinkGem 2016-5-24 来自 MyBatisSpring1.2.3版本 
 */  
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;  
  
  //EnvironmentAware requires spring 3.1  
  private String environment = SqlSessionFactoryBean.class.getSimpleName();  
  
  private boolean failFast;  
  
  private Interceptor[] plugins;  
  
  private TypeHandler<?>[] typeHandlers;  
  
  private String typeHandlersPackage;  
  
  private Class<?>[] typeAliases;  
  
  private String typeAliasesPackage;  
  
  private Class<?> typeAliasesSuperType;  
  
  //issue #19. No default provider.  
  private DatabaseIdProvider databaseIdProvider;  
  
  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)) {  
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,  
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
      for (String packageToScan : typeAliasPackageArray) {  
        // ThinkGem 修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动  
        try {  
            configuration.getTypeAliasRegistry().registerAliases(packageToScan,  
                    typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);  
        } catch (Exception ex) {  
            LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);  
            throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);  
        } finally {  
            ErrorContext.instance().reset();  
        }  
        // ThinkGem end  
        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();  
    }  
  
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));  
  
    if (this.databaseIdProvider != null) {  
      try {  
        configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));  
      } catch (SQLException e) {  
        throw new NestedIOException("Failed getting a databaseId", e);  
      }  
    }  
  
    if (!isEmpty(this.mapperLocations)) {  
      for (Resource mapperLocation : this.mapperLocations) {  
        if (mapperLocation == null) {  
          continue;  
        }  
  
        try {  
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),  
              configuration, mapperLocation.toString(), configuration.getSqlFragments());  
          xmlMapperBuilder.parse();  
        } catch (Exception e) {  
            // ThinkGem MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动  
            LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
        } finally {  
          ErrorContext.instance().reset();  
        }  
  
        if (LOGGER.isDebugEnabled()) {  
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");  
        }  
      }  
        
      // ThinkGem 启动刷新MapperXML定时器(有助于开发者调试)。  
      new MapperRefresh(this.mapperLocations, configuration).run();  
        
    } else {  
      if (LOGGER.isDebugEnabled()) {  
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");  
      }  
    }  
  
    return this.sqlSessionFactoryBuilder.build(configuration);  
  }  
  
  /** 
   * {@inheritDoc} 
   */  
  @Override  
  public SqlSessionFactory getObject() throws Exception {  
    if (this.sqlSessionFactory == null) {  
      afterPropertiesSet();  
    }  
  
    return this.sqlSessionFactory;  
  }  
  
  /** 
   * {@inheritDoc} 
   */  
  @Override  
  public Class<? extends SqlSessionFactory> getObjectType() {  
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();  
  }  
  
  /** 
   * {@inheritDoc} 
   */  
  @Override  
  public boolean isSingleton() {  
    return true;  
  }  
  
  /** 
   * {@inheritDoc} 
   */  
  @Override  
  public void onApplicationEvent(ApplicationEvent event) {  
    if (failFast && event instanceof ContextRefreshedEvent) {  
      // fail-fast -> check all statements are completed  
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();  
    }  
  }  
  
}   

最后附加上属性配置文件:mybatis-refresh.properties

#是否开启刷新线程  
enabled=true  
#延迟启动刷新程序的秒数  
delaySeconds=60  
#刷新扫描间隔的时长秒数  
sleepSeconds=3  
#扫描Mapper文件的资源路径  
mappingPath=mappings  

第三步:配置扫描路径

SpringMVC 和SpringBoot 都是需要配置扫描XML的路径,和mybatis的配置文件,只不过SpringMVC和Springboot配置的方式不同。

(1)SpringMVC 方式

<!-- MyBatis SqlSessionFactoryBean -->  
    <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource"/>  
        <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/>         
        <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/>  
        <property name="configLocation" value="classpath:/mybatis-config.xml"></property>  
    </bean>  

(2)SpringBoot 方式

@Configuration
@MapperScan(basePackages="com.oskyhang.*.mapper", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class DatasourceConfig {

    @Bean(name = "test1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    @Primary
    public DataSource testDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "test1SqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // TODO
        // 加载全局的配置文件
        bean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:sqlMapConfig.xml"));

        Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:com/oskyhang/system/mapper/*.xml");
        bean.setMapperLocations(resources);

        return bean.getObject();
    }

    @Bean(name = "test1TransactionManager")
    @Primary
    public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "test1SqlSessionTemplate")
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
原文地址:https://www.cnblogs.com/oskyhg/p/8587701.html