我们公司的项目比较大,启动起来特别慢,基本得5分钟左右,改其他东西还好,但是改sql时太痛苦了,改完一个,重启,等待,登录,找到刚才的模块,测试,擦,少考虑一个东西,还得改,重启,等待。。。。。。
然后就在网上搜一下有没有不用重启的办法,还真有,https://mp.csdn.net/mdeditor#
我是基于这个修改一个开发环境比较适用的版本
首先说一下这篇文章里的配置
配置我是放在spring-servlet.xml中,sqlSessionFactoryPay 取的值是spring-context.xml中mybatis 配置的bean 的id
然后把sqlSessionFactory,packageSearchPath 改成静态的,在把该类改成单例的,方便调用
刷新的类
package com.kyee.framework.config;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
public class RefreshMapperCache {
private Log log = LogFactory.getLog(RefreshMapperCache.class);
private static SqlSessionFactory sqlSessionFactory;
private Resource[] mapperLocations;
private static String packageSearchPath;
private HashMap<String, Long> fileMapping = new HashMap<String, Long>();// 记录文件是否变化
//记录发生改变的xml文件名称
private List<String> changeResourceNameList = new ArrayList<>();
private static RefreshMapperCache singletonHungary = new RefreshMapperCache();
public static RefreshMapperCache getInstance() {
return singletonHungary;
}
public RefreshMapperCache(){
}
public void refreshMapper() {
try {
Configuration configuration = this.sqlSessionFactory.getConfiguration();
// step.1 扫描文件
try {
this.scanMapperXml();
} catch (IOException e) {
log.error("packageSearchPath扫描包路径配置错误");
return;
}
// System.out.println("==============刷新前mapper中的内容 start===============");
// //获取xml中的每个语句的名称即 id = "findUserById";
// for (String name : configuration.getMappedStatementNames()) {
// System.out.println(name);
// }
// System.out.println("==============刷新前mapper中的内容 end===============");
//清空被修改过后的文件名称,确保该集合是空的
changeResourceNameList.clear();
// step.2 判断是否有文件发生了变化
if (this.isChanged()) {
// step.2.1 清理
this.removeConfig(configuration);
// step.2.2 重新加载
for (Resource configLocation : mapperLocations) {
try {
//匹配被修改过的mapper文件,如果存在,则重新加载
//如果想要重新加载全部mapper,可以不匹配
if(changeResourceNameList.contains(configLocation.getFilename())){
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configLocation.getInputStream(), configuration, configLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
System.out.println("mapper文件[" + configLocation.getFilename() + "]缓存加载成功");
}
} catch (IOException e) {
System.out.println("mapper文件[" + configLocation.getFilename() + "]不存在或内容格式不对");
continue;
}
}
//清空被修改过后的文件名称
changeResourceNameList.clear();
}
// System.out.println("--------------------------刷新后mapper中的内容 start--------------------------");
// for (String name : configuration.getMappedStatementNames()) {
// System.out.println(name);
// }
// System.out.println("--------------------------刷新后mapper中的内容 end--------------------------");
} catch (Exception e) {
System.out.println("****************刷新缓存异常: "+e.getMessage());
}
}
public void setPackageSearchPath(String packageSearchPath) {
this.packageSearchPath = packageSearchPath;
}
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
/**
* 扫描xml文件所在的路径
* @throws IOException
*/
private void scanMapperXml() throws IOException {
this.mapperLocations = new PathMatchingResourcePatternResolver().getResources(packageSearchPath);
}
/**
* 清空Configuration中几个重要的缓存
* @param configuration
* @throws Exception
*/
private void removeConfig(Configuration configuration) throws Exception {
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");
}
@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();
}
/**
* 判断文件是否发生了变化
* @param
* @return
* @throws IOException
*/
private boolean isChanged() throws IOException {
boolean flag = false;
System.out.println("***************************获取文件名 开始************************************");
for (Resource resource : mapperLocations) {
String resourceName = resource.getFilename();
System.out.println("resourceName == " + resourceName+", path = "+resource.getURL().getPath());
boolean addFlag = !fileMapping.containsKey(resourceName);// 此为新增标识
// 修改文件:判断文件内容是否有变化
Long compareFrame = fileMapping.get(resourceName);
long lastFrame = resource.contentLength() + resource.lastModified();
boolean modifyFlag = null != compareFrame && compareFrame.longValue() != lastFrame;// 此为修改标识
if(addFlag){
System.out.println(" 新增了:==="+ resourceName);
}
if(modifyFlag){
System.out.println(" 修改了:==="+ resourceName);
}
// 新增或是修改时,存储文件
if(addFlag || modifyFlag) {
fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件内容帧值
flag = true;
changeResourceNameList.add(resourceName);
}
}
System.out.println("***************************获取文件名 结束************************************");
return flag;
}
}
调用的地方
RefreshMapperCache.getInstance().refreshMapper();
这样我就可以在需要的重新加载xml的时候,调用一下,或者我在开发某个接口时,在最开始调用一下,接口开发好后,删掉