Browse Source

优化,新增 mapper 自动刷新

青苗 9 years ago
parent
commit
5fbd5dba38

+ 9 - 6
mybatis-plus/src/main/java/com/baomidou/mybatisplus/MybatisConfiguration.java

@@ -31,7 +31,7 @@ import java.util.logging.Logger;
  * @author hubin
  * @Date 2016-01-23
  */
-public class MybatisConfiguration extends Configuration{
+public class MybatisConfiguration extends Configuration {
 
 	protected final Logger logger = Logger.getLogger("MybatisConfiguration");
 
@@ -75,13 +75,16 @@ public class MybatisConfiguration extends Configuration{
 	@Override
 	public void addMappedStatement(MappedStatement ms) {
 		logger.fine(" addMappedStatement: " + ms.getId());
-		if(IS_REFRESH){
-			this.mappedStatements.remove(ms.getId());
-		}else{
-			if (this.mappedStatements.containsKey(ms.getId())){
+		if (IS_REFRESH) {
 			/*
-			 * 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
+			 * 支持是否自动刷新 XML 变更内容,开发环境使用【 注:生产环境勿用!】
 			 */
+			this.mappedStatements.remove(ms.getId());
+		} else {
+			if (this.mappedStatements.containsKey(ms.getId())) {
+				/*
+				 * 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
+				 */
 				logger.severe("mapper[" + ms.getId() + "] is ignored, because it's exists, maybe from xml file");
 				return;
 			}

+ 0 - 226
mybatis-plus/src/main/java/com/baomidou/mybatisplus/refresh/MapperRefresh.java

@@ -1,226 +0,0 @@
-package com.baomidou.mybatisplus.refresh;
-
-import com.baomidou.mybatisplus.MybatisConfiguration;
-import org.apache.ibatis.binding.MapperRegistry;
-import org.apache.ibatis.builder.xml.XMLMapperBuilder;
-import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
-import org.apache.ibatis.executor.ErrorContext;
-import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
-import org.apache.ibatis.io.Resources;
-import org.apache.ibatis.parsing.XNode;
-import org.apache.ibatis.parsing.XPathParser;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.springframework.core.NestedIOException;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.UrlResource;
-import org.springframework.util.ResourceUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.*;
-import java.util.logging.Logger;
-
-/**
- * Created by nieqiurong on 2016/8/25 16:13.
- */
-public class MapperRefresh implements Runnable{
-    protected final Logger logger = Logger.getLogger(MapperRefresh.class.getName());
-    private SqlSessionFactory sqlSessionFactory;
-    private Resource[] mapperLocations;
-    private Long beforeTime = 0L;
-    private Configuration configuration;
-    /**
-     *  是否开启刷新mapper
-     */
-    private boolean enabled;
-    /**
-     * xml文件目录
-     */
-    private Set<String> fileSet;
-    /**
-     * 延迟加载时间
-     */
-    private int delaySeconds = 10;
-    /**
-     * 刷新间隔时间
-     */
-    private int sleepSeconds = 20;
-    /**
-     * 记录jar包存在的mapper
-     */
-    private static Map<String,List<Resource>> jarMapper = new HashMap<String, List<Resource>>();
-
-    public MapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, int delaySeconds, int sleepSeconds,boolean enabled){
-        this.mapperLocations = mapperLocations;
-        this.sqlSessionFactory = sqlSessionFactory;
-        this.delaySeconds = delaySeconds;
-        this.enabled = enabled;
-        this.sleepSeconds = sleepSeconds;
-        this.run();
-    }
-
-    public MapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, boolean enabled){
-        this.mapperLocations = mapperLocations;
-        this.sqlSessionFactory = sqlSessionFactory;
-        this.enabled = enabled;
-        this.run();
-    }
-
-    public void run() {
-        beforeTime = System.currentTimeMillis();
-        if (enabled) {
-            final MapperRefresh runnable = this;
-            new Thread(new Runnable(){
-                @Override
-                public void run() {
-                    if (fileSet == null) {
-                        fileSet = new HashSet<String>();
-                        for (Resource mapperLocation : mapperLocations) {
-                            try {
-                                if (ResourceUtils.isJarURL(mapperLocation.getURL())){
-                                    String key = new UrlResource(ResourceUtils.extractJarFileURL(mapperLocation.getURL())).getFile().getPath();
-                                    fileSet.add(key);
-                                    if(jarMapper.get(key)!=null){
-                                        jarMapper.get(key).add(mapperLocation);
-                                    }else{
-                                        List<Resource> resourcesList = new ArrayList<Resource>();
-                                        resourcesList.add(mapperLocation);
-                                        jarMapper.put(key,resourcesList);
-                                    }
-                                } else {
-                                    fileSet.add(mapperLocation.getFile().getPath());
-                                }
-                            } catch (IOException ioException) {
-                                ioException.printStackTrace();
-                            }
-                        }
-                    }
-                    try {
-                        Thread.sleep(delaySeconds * 1000);
-                    } catch (InterruptedException interruptedException) {
-                        interruptedException.printStackTrace();
-                    }
-                    while (true) {
-                        try {
-                            for (String filePath : fileSet){
-                                File file = new File(filePath);
-                                if(file!=null&&file.isFile()&&file.lastModified()>beforeTime){
-                                    MybatisConfiguration.IS_REFRESH = true;
-                                    List<Resource> removeList = jarMapper.get(filePath);
-                                    if(removeList!=null&&!removeList.isEmpty()){//如果是jar包中的xml,将刷新jar包中存在的所有xml,后期再修改加载jar中修改过后的xml
-                                        for(Resource resource:removeList){
-                                            runnable.refresh(resource);
-                                        }
-                                    }else{
-                                        runnable.refresh(new FileSystemResource(file));
-                                    }
-                                }
-                            }
-                            if(MybatisConfiguration.IS_REFRESH)beforeTime = System.currentTimeMillis();
-                            MybatisConfiguration.IS_REFRESH = false;
-                        } catch (Exception exception) {
-                            exception.printStackTrace();
-                        }
-                        try {
-                            Thread.sleep(sleepSeconds * 1000);
-                        } catch (InterruptedException interruptedException) {
-                            interruptedException.printStackTrace();
-                        }
-
-                    }
-                }
-            },"mybatis-plus MapperRefresh").start();
-        }
-    }
-
-    /**
-     * 刷新mapper
-     * @throws Exception
-     */
-    private void refresh(Resource resource) throws Exception {
-        this.configuration = sqlSessionFactory.getConfiguration();
-        boolean isSupper = configuration.getClass().getSuperclass()==Configuration.class;
-        try {
-            Field loadedResourcesField = isSupper?configuration.getClass().getSuperclass().getDeclaredField("loadedResources"):configuration.getClass().getDeclaredField("loadedResources");
-            loadedResourcesField.setAccessible(true);
-            Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));
-            XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(),new XMLMapperEntityResolver());
-            XNode context = xPathParser.evalNode("/mapper");
-            String namespace = context.getStringAttribute("namespace");
-            Field field = MapperRegistry.class.getDeclaredField("knownMappers");
-            field.setAccessible(true);
-            Map mapConfig = (Map) field.get(configuration.getMapperRegistry());
-            mapConfig.remove(Resources.classForName(namespace));
-            loadedResourcesSet.remove(resource.toString());
-            configuration.getCacheNames().remove(namespace);
-            cleanParameterMap(context.evalNodes("/mapper/parameterMap"),namespace);
-            cleanResultMap(context.evalNodes("/mapper/resultMap"),namespace);
-            cleanKeyGenerators(context.evalNodes("insert|update"),namespace);
-            cleanSqlElement(context.evalNodes("/mapper/sql"),namespace);
-            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), sqlSessionFactory.getConfiguration(),   //注入的sql先不进行处理了
-                    resource.toString(), sqlSessionFactory.getConfiguration().getSqlFragments());
-            xmlMapperBuilder.parse();
-            logger.info("refresh:"+resource+",success!");
-        } catch (Exception e) {
-            throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);
-        } finally {
-            ErrorContext.instance().reset();
-        }
-    }
-
-    /**
-     * 清理parameterMap
-     * @param list
-     * @param namespace
-     */
-    private void cleanParameterMap(List<XNode> list,String namespace){
-        for (XNode parameterMapNode : list) {
-            String id = parameterMapNode.getStringAttribute("id");
-            configuration.getParameterMaps().remove(namespace+"."+id);
-        }
-    }
-
-    /**
-     * 清理resultMap
-     * @param list
-     * @param namespace
-     */
-    private void cleanResultMap(List<XNode> list,String namespace){
-        for (XNode resultMapNode : list) {
-            String id = resultMapNode.getStringAttribute("id",
-                    resultMapNode.getValueBasedIdentifier());
-            configuration.getResultMapNames().remove(id);
-            configuration.getResultMapNames().remove(namespace+"."+id);
-        }
-    }
-
-    /**
-     * 清理selectKey
-     * @param list
-     * @param namespace
-     */
-    private void cleanKeyGenerators(List<XNode> list,String namespace){
-        for (XNode context : list) {
-            String id = context.getStringAttribute("id");
-            configuration.getKeyGeneratorNames().remove(id+SelectKeyGenerator.SELECT_KEY_SUFFIX);
-            configuration.getKeyGeneratorNames().remove(namespace+"."+id+SelectKeyGenerator.SELECT_KEY_SUFFIX);
-        }
-    }
-
-    /**
-     * 清理sql节点缓存
-     * @param list
-     * @param namespace
-     */
-    private void cleanSqlElement(List<XNode> list,String namespace){
-        for (XNode context : list) {
-            String id = context.getStringAttribute("id");
-            configuration.getSqlFragments().remove(id);
-            configuration.getSqlFragments().remove(namespace+"."+id);
-        }
-    }
-
-}

+ 271 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/spring/MybatisMapperRefresh.java

@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ *
+ * 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.baomidou.mybatisplus.spring;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.apache.ibatis.binding.MapperRegistry;
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
+import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
+import org.apache.ibatis.executor.ErrorContext;
+import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.parsing.XPathParser;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.core.NestedIOException;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.util.ResourceUtils;
+
+import com.baomidou.mybatisplus.MybatisConfiguration;
+
+/**
+ * <p>
+ * 切莫用于生产环境(后果自负)<br>
+ * Mybatis 映射文件热加载(发生变动后自动重新加载).<br>
+ * 方便开发时使用,不用每次修改xml文件后都要去重启应用.<br>
+ * </p>
+ * 
+ * @author nieqiurong
+ * @Date 2016-08-25
+ */
+public class MybatisMapperRefresh implements Runnable {
+	protected final Logger logger = Logger.getLogger("MybatisMapperRefresh");
+	private SqlSessionFactory sqlSessionFactory;
+	private Resource[] mapperLocations;
+	private Long beforeTime = 0L;
+	private Configuration configuration;
+
+	/**
+	 * 是否开启刷新mapper
+	 */
+	private boolean enabled;
+
+	/**
+	 * xml文件目录
+	 */
+	private Set<String> fileSet;
+
+	/**
+	 * 延迟加载时间
+	 */
+	private int delaySeconds = 10;
+
+	/**
+	 * 刷新间隔时间
+	 */
+	private int sleepSeconds = 20;
+
+	/**
+	 * 记录jar包存在的mapper
+	 */
+	private static Map<String, List<Resource>> jarMapper = new HashMap<String, List<Resource>>();
+
+	public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, int delaySeconds,
+			int sleepSeconds, boolean enabled) {
+		this.mapperLocations = mapperLocations;
+		this.sqlSessionFactory = sqlSessionFactory;
+		this.delaySeconds = delaySeconds;
+		this.enabled = enabled;
+		this.sleepSeconds = sleepSeconds;
+		this.run();
+	}
+
+	public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, boolean enabled) {
+		this.mapperLocations = mapperLocations;
+		this.sqlSessionFactory = sqlSessionFactory;
+		this.enabled = enabled;
+		this.run();
+	}
+
+	public void run() {
+		beforeTime = System.currentTimeMillis();
+		if (enabled) {
+			final MybatisMapperRefresh runnable = this;
+			new Thread(new Runnable() {
+
+				public void run() {
+					if (fileSet == null) {
+						fileSet = new HashSet<String>();
+						for (Resource mapperLocation : mapperLocations) {
+							try {
+								if (ResourceUtils.isJarURL(mapperLocation.getURL())) {
+									String key = new UrlResource(ResourceUtils.extractJarFileURL(mapperLocation.getURL())).getFile().getPath();
+									fileSet.add(key);
+									if (jarMapper.get(key) != null) {
+										jarMapper.get(key).add(mapperLocation);
+									} else {
+										List<Resource> resourcesList = new ArrayList<Resource>();
+										resourcesList.add(mapperLocation);
+										jarMapper.put(key, resourcesList);
+									}
+								} else {
+									fileSet.add(mapperLocation.getFile().getPath());
+								}
+							} catch (IOException ioException) {
+								ioException.printStackTrace();
+							}
+						}
+					}
+					try {
+						Thread.sleep(delaySeconds * 1000);
+					} catch (InterruptedException interruptedException) {
+						interruptedException.printStackTrace();
+					}
+					while (true) {
+						try {
+							for (String filePath : fileSet) {
+								File file = new File(filePath);
+								if (file != null && file.isFile() && file.lastModified() > beforeTime) {
+									MybatisConfiguration.IS_REFRESH = true;
+									List<Resource> removeList = jarMapper.get(filePath);
+									if (removeList != null && !removeList.isEmpty()) {// 如果是jar包中的xml,将刷新jar包中存在的所有xml,后期再修改加载jar中修改过后的xml
+										for (Resource resource : removeList) {
+											runnable.refresh(resource);
+										}
+									} else {
+										runnable.refresh(new FileSystemResource(file));
+									}
+								}
+							}
+							if (MybatisConfiguration.IS_REFRESH) {
+								beforeTime = System.currentTimeMillis();
+							}
+							MybatisConfiguration.IS_REFRESH = false;
+						} catch (Exception exception) {
+							exception.printStackTrace();
+						}
+						try {
+							Thread.sleep(sleepSeconds * 1000);
+						} catch (InterruptedException interruptedException) {
+							interruptedException.printStackTrace();
+						}
+
+					}
+				}
+			}, "mybatis-plus MapperRefresh").start();
+		}
+	}
+
+	/**
+	 * 刷新mapper
+	 * 
+	 * @throws Exception
+	 */
+	@SuppressWarnings("rawtypes")
+	private void refresh(Resource resource) throws Exception {
+		this.configuration = sqlSessionFactory.getConfiguration();
+		boolean isSupper = configuration.getClass().getSuperclass() == Configuration.class;
+		try {
+			Field loadedResourcesField = isSupper
+					? configuration.getClass().getSuperclass().getDeclaredField("loadedResources")
+					: configuration.getClass().getDeclaredField("loadedResources");
+			loadedResourcesField.setAccessible(true);
+			Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));
+			XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(),
+					new XMLMapperEntityResolver());
+			XNode context = xPathParser.evalNode("/mapper");
+			String namespace = context.getStringAttribute("namespace");
+			Field field = MapperRegistry.class.getDeclaredField("knownMappers");
+			field.setAccessible(true);
+			Map mapConfig = (Map) field.get(configuration.getMapperRegistry());
+			mapConfig.remove(Resources.classForName(namespace));
+			loadedResourcesSet.remove(resource.toString());
+			configuration.getCacheNames().remove(namespace);
+			cleanParameterMap(context.evalNodes("/mapper/parameterMap"), namespace);
+			cleanResultMap(context.evalNodes("/mapper/resultMap"), namespace);
+			cleanKeyGenerators(context.evalNodes("insert|update"), namespace);
+			cleanSqlElement(context.evalNodes("/mapper/sql"), namespace);
+			XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),
+					sqlSessionFactory.getConfiguration(), // 注入的sql先不进行处理了
+					resource.toString(), sqlSessionFactory.getConfiguration().getSqlFragments());
+			xmlMapperBuilder.parse();
+			logger.fine("refresh:" + resource + ",success!");
+		} catch (Exception e) {
+			throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);
+		} finally {
+			ErrorContext.instance().reset();
+		}
+	}
+
+	/**
+	 * 清理parameterMap
+	 * 
+	 * @param list
+	 * @param namespace
+	 */
+	private void cleanParameterMap(List<XNode> list, String namespace) {
+		for (XNode parameterMapNode : list) {
+			String id = parameterMapNode.getStringAttribute("id");
+			configuration.getParameterMaps().remove(namespace + "." + id);
+		}
+	}
+
+	/**
+	 * 清理resultMap
+	 * 
+	 * @param list
+	 * @param namespace
+	 */
+	private void cleanResultMap(List<XNode> list, String namespace) {
+		for (XNode resultMapNode : list) {
+			String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
+			configuration.getResultMapNames().remove(id);
+			configuration.getResultMapNames().remove(namespace + "." + id);
+		}
+	}
+
+	/**
+	 * 清理selectKey
+	 * 
+	 * @param list
+	 * @param namespace
+	 */
+	private void cleanKeyGenerators(List<XNode> list, String namespace) {
+		for (XNode context : list) {
+			String id = context.getStringAttribute("id");
+			configuration.getKeyGeneratorNames().remove(id + SelectKeyGenerator.SELECT_KEY_SUFFIX);
+			configuration.getKeyGeneratorNames().remove(namespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX);
+		}
+	}
+
+	/**
+	 * 清理sql节点缓存
+	 * 
+	 * @param list
+	 * @param namespace
+	 */
+	private void cleanSqlElement(List<XNode> list, String namespace) {
+		for (XNode context : list) {
+			String id = context.getStringAttribute("id");
+			configuration.getSqlFragments().remove(id);
+			configuration.getSqlFragments().remove(namespace + "." + id);
+		}
+	}
+
+}

+ 0 - 472
mybatis-plus/src/main/java/com/baomidou/mybatisplus/spring/MybatisXMLMapperLoader.java

@@ -1,472 +0,0 @@
-/**
- * Copyright (c) 2011-2020, hubin (jobob@qq.com).
- *
- * 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.baomidou.mybatisplus.spring;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.ibatis.builder.xml.XMLMapperBuilder;
-import org.apache.ibatis.executor.ErrorContext;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.mybatis.spring.mapper.MapperScannerConfigurer;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.DisposableBean;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.core.io.support.ResourcePatternResolver;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.StopWatch;
-import org.springframework.util.StringUtils;
-
-/**
- * <p>
- * 切莫用于生产环境(后果自负)<br>
- * mybatis映射文件热加载(发生变动后自动重新加载).<br>
- * 方便开发时使用,不用每次修改xml文件后都要去重启应用.<br>
- * 特性:<br>
- * 1.支持不同的数据源。<br>
- * 2.双线程实时监控,一个用来监控全局,一个用来实时监控热点文件。(100ms)(热点文件2分钟内没续修改自动过期)<br>
- * 3.对于CPU不给力和映射文件庞大的应用,有一定程度的性能问题。<br>
- * </p>
- * 
- * @author hubin
- * @Date 2016-08-25
- */
-public class MybatisXMLMapperLoader implements DisposableBean, InitializingBean, ApplicationContextAware {
-
-	private ApplicationContext applicationContext;
-	private ScheduledExecutorService pool;
-
-	// 多数据源的场景使用
-	private Boolean enableAutoReload = true; // 是否启用热加载.
-	private String mapperLocations; // 指定映射配置文件
-	private MapperScannerConfigurer config;
-	private SqlSessionFactory sqlSessionFactory;
-
-	/**
-	 * 是否启用热加载.
-	 * 
-	 * @param config
-	 */
-	public void setEnableAutoReload(Boolean enableAutoReload) {
-		this.enableAutoReload = enableAutoReload;
-	}
-
-	/**
-	 * 指定映射配置文件.
-	 * 
-	 * @param config
-	 */
-	public void setMapperLocations(String mapperLocations) {
-		if (!StringUtils.isEmpty(mapperLocations)) {
-			this.mapperLocations = mapperLocations;
-		}
-	}
-
-	/**
-	 * 设置配置对象.
-	 *
-	 * @param config
-	 */
-	public void setConfig(MapperScannerConfigurer config) {
-		this.config = config;
-	}
-
-	/**
-	 * 设置数据源.
-	 *
-	 * @param sqlSessionFactory
-	 */
-	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
-		this.sqlSessionFactory = sqlSessionFactory;
-	}
-
-	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-		this.applicationContext = applicationContext;
-	}
-
-	public void afterPropertiesSet() throws Exception {
-
-		// 检查设置
-		if (!enableAutoReload) {
-			System.out.println("禁用:mybatis自动热加载!");
-			return;
-		} else {
-			System.out.println("启用:mybatis自动热加载");
-		}
-
-		checkProperties(); // 检查属性
-
-		// 获取mapperLocations
-		String mapperLocations = getMapperLocations();
-
-		// 初始化线程池2个(避免线程来回切换)(一个用来监控全局,一个用来实时监控热点文件.)
-		pool = Executors.newScheduledThreadPool(2);
-
-		// 配置扫描器.
-		final AutoReloadScanner scaner = new AutoReloadScanner(mapperLocations);
-		scaner.start();
-
-		// 扫描全部(2s一次)
-		pool.scheduleAtFixedRate(new Runnable() {
-			public void run() {
-				scaner.scanAllFileChange();
-			}
-		}, 2, 2, TimeUnit.SECONDS);
-
-		// 扫描热点文件(100ms一次,监控更为频繁)
-		pool.scheduleAtFixedRate(new Runnable() {
-			public void run() {
-				scaner.scanHotspotFileChange();
-			}
-		}, 2, 100, TimeUnit.MILLISECONDS);
-
-		System.out.println("启动mybatis自动热加载");
-	}
-
-	/**
-	 * 获取配置文件路径(mapperLocations).
-	 *
-	 * @return
-	 * @throws NoSuchFieldException
-	 * @throws IllegalAccessException
-	 * @throws Exception
-	 */
-	private String getMapperLocations() throws NoSuchFieldException, IllegalAccessException, Exception {
-
-		// 优先使用mapperLocations
-		if (mapperLocations != null) {
-			return mapperLocations;
-		}
-
-		// 从MapperScannerConfigurer中获取.
-		if (config != null) {
-			Field field = config.getClass().getDeclaredField("basePackage");
-			field.setAccessible(true);
-			return (String) field.get(config);
-		}
-
-		// 根本就获取不到 org.mybatis.spring.SqlSessionFactoryBean
-		// spring的org.springframework.beans.factory.FactoryBean 将其封装成为了一个
-		// org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
-		// 牛逼的spring
-		// if(sqlSessionFactory != null){
-		// Field field =
-		// sqlSessionFactory.getClass().getDeclaredField("mapperLocations");
-		// field.setAccessible(true);
-		// Resource[] mapperLocations = (Resource[])
-		// field.get(sqlSessionFactory);
-		// StringBuilder sb = new StringBuilder();
-		// for(Resource r : mapperLocations){
-		// String n = r.getURL().toString();
-		// sb.append(n).append("\n");
-		// }
-		// return sb.toString();
-		// }
-		throw new RuntimeException("获取mapperLocations失败!");
-	}
-
-	/**
-	 * 检查属性,如果没有设置,直接初始化成默认的方式.
-	 */
-	private void checkProperties() {
-		// 如果没有指定数据源,直接使用默认的方式获取数据源
-		if (sqlSessionFactory == null) {
-			try {
-				sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
-			} catch (BeansException e) {
-				throw new RuntimeException("获取数据源失败!", e);
-			}
-		}
-
-		// 如果没有指定配置文件,使用默认的方式获取配置文件
-		if (config == null && mapperLocations == null) {
-			try {
-				config = applicationContext.getBean(MapperScannerConfigurer.class);
-			} catch (BeansException e) {
-				System.err.println("获取配置文件失败!");
-			}
-		}
-
-		if (config == null && mapperLocations == null) {
-			throw new RuntimeException("设置配置mapperLocations失败!,请设置好配置属性,否则自动热加载无法起作用!");
-		}
-	}
-
-	public void destroy() throws Exception {
-		if (pool == null) {
-			return;
-		}
-		pool.shutdown(); // 是否线程池资源
-	}
-
-	/**
-	 * 自动重载扫描器的具体实现
-	 *
-	 *
-	 * @author thomas
-	 * @date Mar 31, 2016 6:59:34 PM
-	 *
-	 */
-	class AutoReloadScanner {
-
-		static final String XML_RESOURCE_PATTERN = "**/*.xml";
-		static final String CLASSPATH_ALL_URL_PREFIX = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
-
-		static final int expireTimes = 600 * 2; // 2分钟内没有继续修改,变成非热点文件.不进行实时监控.
-
-		// 需要扫描的包
-		String[] basePackages;
-
-		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
-
-		// 所有文件
-		Map<String, String> files = new ConcurrentHashMap<String, String>();
-
-		// 热点文件.
-		Map<String, AtomicInteger> hotspot = new ConcurrentHashMap<String, AtomicInteger>();
-
-		public AutoReloadScanner(String basePackage) {
-			basePackages = StringUtils.tokenizeToStringArray(basePackage,
-					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
-		}
-
-		/**
-		 * 只扫描热点文件改变.(热点文件失效:连续600个扫描周期内(1分钟)没有改变)
-		 */
-		public void scanHotspotFileChange() {
-
-			// 如果热点文件为空,立即返回.
-			if (hotspot.isEmpty()) {
-				return;
-			}
-
-			List<String> list = new ArrayList<String>();
-			for (Map.Entry<String, AtomicInteger> e : hotspot.entrySet()) {
-				String url = e.getKey();
-				AtomicInteger counter = e.getValue();
-				if (counter.incrementAndGet() >= expireTimes) {
-					// 计数器自增,判断是否超过指定的过期次数
-					list.add(url);
-				}
-				if (hasChange(url, files.get(url))) {
-					reload(url); // 变化,调用重新加载方法
-					counter.set(0); // 计数器清零
-				}
-			}
-
-			// 移除过期的热点文件
-			if (!list.isEmpty()) {
-				// System.out.println("移除过期的热点文件:list=" + list);
-				for (String s : list) {
-					hotspot.remove(s);
-				}
-			}
-		}
-
-		/**
-		 * 重新加载文件.
-		 *
-		 * @param url
-		 */
-		private void reload(String url) {
-			reloadAll(); // 必须加载所有文件,否则其它文件由于没有加载会导致找不到对应的语句异常(暂时先这样吧)
-		}
-
-		/**
-		 * 重新加载所有文件.
-		 */
-		private void reloadAll() {
-			StopWatch sw = new StopWatch("mybatis mapper auto reload");
-			sw.start();
-			Configuration configuration = getConfiguration();
-			for (Map.Entry<String, String> entry : files.entrySet()) {
-				String location = entry.getKey();
-				Resource r = resourcePatternResolver.getResource(location);
-				try {
-					XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(r.getInputStream(), configuration,
-							r.toString(), configuration.getSqlFragments());
-					xmlMapperBuilder.parse();
-				} catch (Exception e) {
-					throw new RuntimeException("Failed to parse mapping resource: '" + r + "'", e);
-				} finally {
-					ErrorContext.instance().reset();
-				}
-			}
-			sw.stop();
-			// System.out.println("重新加载mybatis映射文件完成.");
-			System.out.println(sw.shortSummary());
-		}
-
-		/**
-		 * 扫描所有文件改变.
-		 */
-		public void scanAllFileChange() {
-			for (Map.Entry<String, String> entry : files.entrySet()) {
-				String url = entry.getKey();
-				if (hasChange(url, entry.getValue())) {
-					// 变化,判断是否在热点文件中,如果存在,直接忽略,如果不存在,触发重新加载
-					if (!hotspot.containsKey(url)) {
-						// 添加到热点文件,并且触发重新加载
-						hotspot.put(url, new AtomicInteger(0));
-						reload(url);
-					}
-				}
-			}
-		}
-
-		/**
-		 * 判断文件是否变化.
-		 *
-		 * @param url
-		 * @param tag
-		 * @return
-		 */
-		private boolean hasChange(String url, String tag) {
-			Resource r = resourcePatternResolver.getResource(url);
-			String newTag = getTag(r);
-			// 之前的标记和最新的标记不一致,说明文件修改了!
-			if (!tag.equals(newTag)) {
-				files.put(url, newTag); // 更新标记
-				return true;
-			}
-			return false;
-		}
-
-		/**
-		 * 获得文件的标记.
-		 * 
-		 * @param r
-		 * @return
-		 */
-		private String getTag(Resource r) {
-			try {
-				StringBuilder sb = new StringBuilder();
-				sb.append(r.contentLength());
-				sb.append(r.lastModified());
-				return sb.toString();
-			} catch (IOException e) {
-				throw new RuntimeException("获取文件标记信息失败!r=" + r, e);
-			}
-		}
-
-		/**
-		 * 开启扫描服务
-		 */
-		public void start() {
-			try {
-				for (String basePackage : basePackages) {
-					Resource[] resources = getResource(basePackage);
-					if (resources != null) {
-						for (Resource r : resources) {
-							String tag = getTag(r);
-							files.put(r.getURL().toString(), tag);
-						}
-					}
-				}
-			} catch (Exception e) {
-				throw new RuntimeException("初始化扫描服务失败!", e);
-			}
-		}
-
-		/**
-		 * 获取xml文件资源.
-		 *
-		 * @param basePackage
-		 * @param pattern
-		 * @return
-		 * @throws IOException
-		 */
-		public Resource[] getResource(String basePackage) {
-			try {
-				if (!basePackage.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
-					basePackage = CLASSPATH_ALL_URL_PREFIX
-							+ ClassUtils.convertClassNameToResourcePath(
-									applicationContext.getEnvironment().resolveRequiredPlaceholders(basePackage))
-							+ "/" + XML_RESOURCE_PATTERN;
-				}
-				Resource[] resources = resourcePatternResolver.getResources(basePackage);
-				return resources;
-			} catch (Exception e) {
-				throw new RuntimeException("获取xml文件资源失败!basePackage=" + basePackage, e);
-			}
-		}
-
-		/**
-		 * 获取配置信息,必须每次都重新获取,否则重新加载xml不起作用.
-		 * 
-		 * @return
-		 */
-		private Configuration getConfiguration() {
-			Configuration configuration = sqlSessionFactory.getConfiguration();
-			removeConfig(configuration);
-			return configuration;
-		}
-
-		/**
-		 * 删除不必要的配置项.
-		 * 
-		 * @param configuration
-		 * @throws Exception
-		 */
-		private void removeConfig(Configuration configuration) {
-			try {
-				Class<?> classConfig = configuration.getClass();
-				clearMap(classConfig, configuration, "mappedStatements");
-				clearMap(classConfig, configuration, "caches");
-				clearMap(classConfig, configuration, "resultMaps");
-				clearMap(classConfig, configuration, "parameterMaps");
-				clearMap(classConfig, configuration, "keyGenerators");
-				clearMap(classConfig, configuration, "sqlFragments");
-				clearSet(classConfig, configuration, "loadedResources");
-			} catch (Exception e) {
-				e.printStackTrace();
-			}
-		}
-
-		@SuppressWarnings("rawtypes")
-		private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {
-			Field field = classConfig.getDeclaredField(fieldName);
-			field.setAccessible(true);
-			Map mapConfig = (Map) field.get(configuration);
-			mapConfig.clear();
-		}
-
-		@SuppressWarnings("rawtypes")
-		private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {
-			Field field = classConfig.getDeclaredField(fieldName);
-			field.setAccessible(true);
-			Set setConfig = (Set) field.get(configuration);
-			setConfig.clear();
-		}
-
-	}
-
-}

+ 1 - 1
mybatis-plus/src/main/java/com/baomidou/mybatisplus/toolkit/DBKeywordsProcessor.java

@@ -46,7 +46,7 @@ public class DBKeywordsProcessor {
 				KEYWORDS.add(keyword);
 			}
 		} catch (Exception e) {
-			logger.severe("If you want to support the keyword query, must have database_keywords.dic \n"
+			logger.warning("If you want to support the keyword query, must have database_keywords.dic \n"
 					+ e.getMessage());
 		} finally {
 			if (br != null) {

+ 77 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/MybatisMapperRefreshTest.java

@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ *
+ * 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.baomidou.mybatisplus.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+import com.baomidou.mybatisplus.MybatisSessionFactoryBuilder;
+import com.baomidou.mybatisplus.plugins.pagination.Pagination;
+import com.baomidou.mybatisplus.spring.MybatisMapperRefresh;
+import com.baomidou.mybatisplus.test.mysql.MySqlInjector;
+import com.baomidou.mybatisplus.test.mysql.UserMapper;
+import com.baomidou.mybatisplus.test.mysql.UserMapperTest;
+
+/**
+ * <p>
+ * 切莫用于生产环境(后果自负)<br>
+ * Mybatis 映射文件热加载(发生变动后自动重新加载).<br>
+ * 方便开发时使用,不用每次修改xml文件后都要去重启应用.<br>
+ * </p>
+ * 
+ * @author nieqiurong
+ * @Date 2016-08-25
+ */
+public class MybatisMapperRefreshTest {
+
+	/**
+	 * 测试 Mybatis XML 修改自动刷新
+	 */
+	public static void main(String[] args) throws IOException, InterruptedException {
+		InputStream in = UserMapperTest.class.getClassLoader().getResourceAsStream("mysql-config.xml");
+		MybatisSessionFactoryBuilder mf = new MybatisSessionFactoryBuilder();
+		mf.setSqlInjector(new MySqlInjector());
+		Resource[] resource = new ClassPathResource[] { new ClassPathResource("mysql/UserMapper.xml") };
+		SqlSessionFactory sessionFactory = mf.build(in);
+		new MybatisMapperRefresh(resource, sessionFactory, 0, 5, true);
+		boolean isReturn = false;
+		SqlSession session = null;
+		while (!isReturn) {
+			try {
+				session = sessionFactory.openSession();
+				UserMapper userMapper = session.getMapper(UserMapper.class);
+				userMapper.selectListRow(new Pagination(1, 10));
+				resource[0].getFile().setLastModified(System.currentTimeMillis());
+				session.commit();
+				session.close();
+				Thread.sleep(5000);
+			} catch (Exception e) {
+				e.printStackTrace();
+			} finally {
+				if (session != null) {
+					session.close();
+				}
+				Thread.sleep(5000);
+			}
+		}
+		System.exit(0);
+	}
+}

+ 0 - 50
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/refresh/MapperRefreshTest.java

@@ -1,50 +0,0 @@
-package com.baomidou.mybatisplus.test.refresh;
-
-import com.baomidou.mybatisplus.MybatisSessionFactoryBuilder;
-import com.baomidou.mybatisplus.plugins.pagination.Pagination;
-import com.baomidou.mybatisplus.refresh.MapperRefresh;
-import com.baomidou.mybatisplus.test.mysql.MySqlInjector;
-import com.baomidou.mybatisplus.test.mysql.UserMapper;
-import com.baomidou.mybatisplus.test.mysql.UserMapperTest;
-import org.apache.ibatis.session.SqlSession;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Created by nieqiurong on 2016/8/26 6:36.
- */
-public class MapperRefreshTest {
-    public static void main(String[] args) throws IOException, InterruptedException {
-
-        InputStream in = UserMapperTest.class.getClassLoader().getResourceAsStream("mysql-config.xml");
-        MybatisSessionFactoryBuilder mf = new MybatisSessionFactoryBuilder();
-        mf.setSqlInjector(new MySqlInjector());
-        Resource[] resource = new ClassPathResource[]{new ClassPathResource("mysql/UserMapper.xml")};
-        SqlSessionFactory sessionFactory = mf.build(in);
-        new MapperRefresh(resource,sessionFactory,0,5);
-        boolean isReturn = false;
-        SqlSession session=null;
-        while (!isReturn){
-            try {
-                session = sessionFactory.openSession();
-                UserMapper userMapper = session.getMapper(UserMapper.class);
-                userMapper.selectListRow(new Pagination(1,10));
-                resource[0].getFile().setLastModified(System.currentTimeMillis());
-                session.commit();
-                session.close();
-                Thread.sleep(5000);
-            }catch (Exception e){
-                e.printStackTrace();
-//                isReturn = true;
-            }finally {
-                if(session!=null)session.close();
-                Thread.sleep(5000);
-            }
-        }
-        System.exit(0);
-    }
-}

+ 1 - 2
mybatis-plus/src/test/resources/mysql/UserMapper.xml

@@ -7,7 +7,6 @@
 
 	<!-- 建议字段,采用驼峰命名方法,不然很麻烦 -->
 	<select id="selectListRow" resultType="User">
-		select test_id AS
-		id,name,age,test_type AS testType from user
+		select test_id AS id,name,age,test_type AS testType from user
 	</select>
 </mapper>

+ 28 - 33
mybatis-plus/src/test/resources/wiki/spring-config.md

@@ -41,16 +41,35 @@
 <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 	<property name="basePackage" value="xxx.mapper" />
 </bean>
+```
 
-<!-- MyBatis 自动热加载 -->
-<bean class="com.baomidou.mybatisplus.spring.MybatisXMLMapperLoader" >
-    <!-- 设置是否启用: 默认启用 -->
-    <property name="enableAutoReload" value="true" />
-    <!-- 设置sqlSessionFactory -->
-    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
-    <!-- 设置映射文件地址 -->
-    <property name="mapperLocations" value="classpath*:xx/mapper/*.xml" />
-</bean>
+> 开启动态加载 mapper
+
+```
+
+    参数说明:
+        sqlSessionFactory:session工厂
+        mapperLocations:mapper匹配路径
+        enabled:是否开启动态加载  默认:false
+        delaySeconds:项目启动延迟加载时间  单位:秒  默认:10s
+        sleepSeconds:刷新时间间隔  单位:秒 默认:20s
+    提供了两个构造,挑选一个配置进入spring配置文件即可:
+
+	构造1:
+	    <bean class="com.baomidou.mybatisplus.spring.MybatisMapperRefresh">
+	        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
+	        <constructor-arg name="mapperLocations" value="classpath*:mybatis/mappers/*/*.xml"/>
+	        <constructor-arg name="enabled" value="true"/>
+	    </bean>
+	
+	构造2:
+		<bean class="com.baomidou.mybatisplus.spring.MybatisMapperRefresh">
+	        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
+	        <constructor-arg name="mapperLocations" value="classpath*:mybatis/mappers/*/*.xml"/>
+	        <constructor-arg name="delaySeconds" value="10"/>
+	        <constructor-arg name="sleepSeconds" value="20"/>
+	        <constructor-arg name="enabled" value="true"/>
+	    </bean>
 ```
 
 
@@ -119,30 +138,6 @@
 	| -->
 </configuration>
 ```
-...
-开启动态加载mapper
-    参数说明:
-        sqlSessionFactory:session工厂
-        mapperLocations:mapper匹配路径
-        enabled:是否开启动态加载  默认:false
-        delaySeconds:项目启动延迟加载时间  单位:秒  默认:10s
-        sleepSeconds:刷新时间间隔  单位:秒 默认:20s
-    提供了两个构造,挑选一个配置进入spring配置文件即可:
-    构造1:
-    <bean class="com.baomidou.mybatisplus.refresh.MapperRefresh">
-        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
-        <constructor-arg name="mapperLocations" value="classpath*:mybatis/mappers/*/*.xml"/>
-        <constructor-arg name="enabled" value="true"/>
-    </bean>
-    构造2:
-     <bean class="com.baomidou.mybatisplus.refresh.MapperRefresh">
-         <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
-         <constructor-arg name="mapperLocations" value="classpath*:mybatis/mappers/*/*.xml"/>
-         <constructor-arg name="delaySeconds" value="10"/>
-         <constructor-arg name="sleepSeconds" value="20"/>
-         <constructor-arg name="enabled" value="true"/>
-     </bean>
-...