1、将springloaded-1.2.5.RELEASE.jar(http://pan.baidu.com/s/1o7oRq1k)放在tomcat的bin目录下
2、修改bin目录下的catalina.bat,在第一行加上下面这一句,注意路径可能需要修改一下:
set JAVA_OPTS=-javaagent:E:apache-tomcat-7.0.55inspringloaded-1.2.5.RELEASE.jar -noverify
如此以来,所有的class文件更新都会实时反馈,但是新增的类文件的注解并不能生效
如果是想在eclipse的tomcat中使用,则在run configruation的相应tomcat的arguments的VM arguments中另起一行添加一个变量:-Djavaagent:D:softDevelopSoft oolsjarspringloaded-1.2.5.RELEASE.jar -noverify 即可
注意eclipse中的tomcat默认是代码修改就会重启的,这和我们的热部署是冲突的,所以我们要禁用tomcat的自动重启:
去掉对号:
server.xml的reloadable设为false
3、对于mybatis的xml,暂时用手动来触发更新,可以参考SysUtilsController.java中的refreshMappers方法,手动来触发该方法就会重新加载所有spring配置文件中指定路径的xml
【SysUtilsController】
1 /** 2 * 3 * @ClassName: SysUtilsController 4 * @Description: TODO 5 * @author: liuyx 6 * @date: 2016年1月5日下午5:43:52 7 */ 8 @Controller 9 @RequestMapping("/sysUtils") 10 public class SysUtilsController extends BaseController{ 11 // 日志记录器 12 private final Logger logger = Logger.getLogger(this.getClass()); 13 14 /** 15 * 16 * @Title: refreshMappers 17 * @author:liuyx 18 * @date:2015年12月30日下午6:39:09 19 * @Description: 重新加载所有mapper xml文件 20 * @param request 21 * @return 22 */ 23 @RequestMapping("/refreshMappers") 24 @ResponseBody 25 public RetObj refreshMappers(HttpServletRequest request) { 26 MybatisMapperLoadUtil mapperUtil = (MybatisMapperLoadUtil)ComponentFactory.getBean(MybatisMapperLoadUtil.class); 27 try { 28 mapperUtil.getScanner().reloadXML(); 29 } catch (Exception e) { 30 // TODO Auto-generated catch block 31 e.printStackTrace(); 32 logger.error(e); 33 return new RetObj(false,request); 34 } 35 return new RetObj(true,request); 36 } 37 38 }
【MybatisMapperLoadUtil】① (这个是根据 XML_RESOURCE_PATTERN 所写的路径扫描所有mapper xml)
1 import java.io.IOException; 2 import java.lang.reflect.Field; 3 import java.util.HashMap; 4 import java.util.Map; 5 import java.util.Set; 6 7 import org.apache.ibatis.builder.xml.XMLMapperBuilder; 8 import org.apache.ibatis.executor.ErrorContext; 9 import org.apache.ibatis.session.Configuration; 10 import org.apache.ibatis.session.SqlSessionFactory; 11 import org.apache.log4j.Logger; 12 import org.springframework.beans.BeansException; 13 import org.springframework.beans.factory.InitializingBean; 14 import org.springframework.context.ApplicationContext; 15 import org.springframework.context.ApplicationContextAware; 16 import org.springframework.context.ConfigurableApplicationContext; 17 import org.springframework.core.io.Resource; 18 import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 19 import org.springframework.core.io.support.ResourcePatternResolver; 20 21 /** 22 * 用于重新加载Mapper的工具 23 */ 24 public class MybatisMapperLoadUtil implements InitializingBean, ApplicationContextAware { 25 // 日志记录器 26 private final Logger logger = Logger.getLogger(this.getClass()); 27 private final HashMap<String, String> mappers = new HashMap<String, String>(); 28 private volatile ConfigurableApplicationContext context = null; 29 private volatile Scanner scanner = null; 30 31 @Override 32 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 33 this.context = (ConfigurableApplicationContext) applicationContext; 34 } 35 36 @Override 37 public void afterPropertiesSet() throws Exception { 38 setScanner(new Scanner()); 39 /*try { 40 41 new Timer(true).schedule(new TimerTask() { 42 public void run() { 43 try { 44 if("off".equals(ConfigService.getConfigRealTime(ConfigService.DYNAMIC_LOAD_MYBATIS_XML_SWITCH))) { 45 46 }else { 47 if (scanner.isChanged()) { 48 System.out.println("load mapper.xml"); 49 scanner.reloadXML(); 50 } 51 } 52 53 } catch (Exception e) { 54 e.printStackTrace(); 55 } 56 } 57 }, 10 * 1000, 5 * 1000); 58 } catch (Exception e1) { 59 e1.printStackTrace(); 60 }*/ 61 } 62 63 public Scanner getScanner() { 64 return scanner; 65 } 66 67 public void setScanner(Scanner scanner) { 68 this.scanner = scanner; 69 } 70 71 @SuppressWarnings("unchecked") 72 public 73 class Scanner { 74 //此处应调整为可配置 75 private static final String XML_RESOURCE_PATTERN = "com/sdyy/**/model/*.xml";//ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "**/*Mapper.xml"; 76 private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 77 public Scanner() throws IOException { 78 Resource[] resources = findResource(); 79 if (resources != null) { 80 for (Resource resource : resources) { 81 String key = resource.getURI().toString(); 82 String value = getMd(resource); 83 mappers.put(key, value); 84 } 85 } 86 } 87 public void reloadXML() throws Exception { 88 SqlSessionFactory factory = context.getBean(SqlSessionFactory.class); 89 Configuration configuration = factory.getConfiguration(); 90 removeConfig(configuration); 91 for (Resource resource : findResource()) { 92 try { 93 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments()); 94 xmlMapperBuilder.parse(); 95 } finally { 96 ErrorContext.instance().reset(); 97 } 98 } 99 } 100 private void removeConfig(Configuration configuration) throws Exception { 101 Class<?> classConfig = configuration.getClass(); 102 clearMap(classConfig, configuration, "mappedStatements"); 103 clearMap(classConfig, configuration, "caches"); 104 clearMap(classConfig, configuration, "resultMaps"); 105 clearMap(classConfig, configuration, "parameterMaps"); 106 clearMap(classConfig, configuration, "keyGenerators"); 107 clearMap(classConfig, configuration, "sqlFragments"); 108 clearSet(classConfig, configuration, "loadedResources"); 109 } 110 private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception { 111 Field field = classConfig.getDeclaredField(fieldName); 112 field.setAccessible(true); 113 ((Map) field.get(configuration)).clear(); 114 } 115 private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception { 116 Field field = classConfig.getDeclaredField(fieldName); 117 field.setAccessible(true); 118 ((Set) field.get(configuration)).clear(); 119 } 120 public boolean isChanged() throws IOException { 121 boolean isChanged = false; 122 for (Resource resource : findResource()) { 123 String key = resource.getURI().toString(); 124 String value = getMd(resource); 125 if (!value.equals(mappers.get(key))) { 126 isChanged = true; 127 mappers.put(key, value); 128 } 129 } 130 return isChanged; 131 } 132 private Resource[] findResource() throws IOException { 133 return resourcePatternResolver.getResources(XML_RESOURCE_PATTERN); 134 } 135 private String getMd(Resource resource) throws IOException { 136 return new StringBuilder().append(resource.contentLength()).append("-").append(resource.lastModified()).toString(); 137 } 138 } 139 }
【MybatisMapperLoadUtil】② (这个是根据 mybatis-config.xml所写的所有mappers的路径更新xml)
1 import java.io.IOException; 2 import java.io.InputStream; 3 import java.lang.reflect.Field; 4 import java.util.Collection; 5 import java.util.HashMap; 6 import java.util.Map; 7 import java.util.Set; 8 9 import org.apache.ibatis.binding.MapperRegistry; 10 import org.apache.ibatis.builder.xml.XMLConfigBuilder; 11 import org.apache.ibatis.builder.xml.XMLMapperBuilder; 12 import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; 13 import org.apache.ibatis.exceptions.ExceptionFactory; 14 import org.apache.ibatis.executor.ErrorContext; 15 import org.apache.ibatis.io.Resources; 16 import org.apache.ibatis.mapping.MappedStatement; 17 import org.apache.ibatis.parsing.XNode; 18 import org.apache.ibatis.parsing.XPathParser; 19 import org.apache.ibatis.session.Configuration; 20 import org.apache.ibatis.session.SqlSessionFactory; 21 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 22 import org.apache.log4j.Logger; 23 import org.springframework.beans.BeansException; 24 import org.springframework.beans.factory.InitializingBean; 25 import org.springframework.context.ApplicationContext; 26 import org.springframework.context.ApplicationContextAware; 27 import org.springframework.context.ConfigurableApplicationContext; 28 import org.springframework.core.io.Resource; 29 import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 30 import org.springframework.core.io.support.ResourcePatternResolver; 31 32 /** 33 * 用于重新加载Mapper的工具 34 */ 35 public class MybatisMapperLoadUtil implements InitializingBean, ApplicationContextAware { 36 // 日志记录器 37 private final Logger logger = Logger.getLogger(this.getClass()); 38 private final HashMap<String, String> mappers = new HashMap<String, String>(); 39 private volatile ConfigurableApplicationContext context = null; 40 private volatile Scanner scanner = null; 41 42 @Override 43 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 44 this.context = (ConfigurableApplicationContext) applicationContext; 45 } 46 47 @Override 48 public void afterPropertiesSet() throws Exception { 49 setScanner(new Scanner()); 50 /*try { 51 52 new Timer(true).schedule(new TimerTask() { 53 public void run() { 54 try { 55 if("off".equals(ConfigService.getConfigRealTime(ConfigService.DYNAMIC_LOAD_MYBATIS_XML_SWITCH))) { 56 57 }else { 58 if (scanner.isChanged()) { 59 System.out.println("load mapper.xml"); 60 scanner.reloadXML(); 61 } 62 } 63 64 } catch (Exception e) { 65 e.printStackTrace(); 66 } 67 } 68 }, 10 * 1000, 5 * 1000); 69 } catch (Exception e1) { 70 e1.printStackTrace(); 71 }*/ 72 } 73 74 public Scanner getScanner() { 75 return scanner; 76 } 77 78 public void setScanner(Scanner scanner) { 79 this.scanner = scanner; 80 } 81 82 @SuppressWarnings("unchecked") 83 public 84 class Scanner { 85 //此处应调整为可配置 86 private static final String XML_RESOURCE_PATTERN = "**/model/*mybatis.xml";//ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "**/*Mapper.xml"; 87 private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 88 public Scanner() throws IOException { 89 Resource[] resources = findResource(); 90 if (resources != null) { 91 for (Resource resource : resources) { 92 String key = resource.getURI().toString(); 93 String value = getMd(resource); 94 mappers.put(key, value); 95 } 96 } 97 } 98 public void reloadXML() throws Exception { 99 SqlSessionFactory factory = context.getBean(SqlSessionFactory.class); 100 Configuration configuration = factory.getConfiguration(); 101 102 removeConfig(configuration); 103 104 105 //获取新的mybatis-config文件配置 2016年1月5日17:49:37 106 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); 107 108 try { 109 XPathParser parser = new XPathParser(inputStream, true, null, new XMLMapperEntityResolver()); 110 for (XNode child : parser.evalNode("/configuration").evalNode("mappers").getChildren()) { 111 String resourcePath = child.getStringAttribute("resource"); 112 Resource resource = resourcePatternResolver.getResource(resourcePath); 113 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments()); 114 xmlMapperBuilder.parse(); 115 116 } 117 } catch (Exception e) { 118 throw ExceptionFactory.wrapException("Error building SqlSession.", e); 119 } finally { 120 ErrorContext.instance().reset(); 121 try { 122 inputStream.close(); 123 } catch (IOException e) { 124 // Intentionally ignore. Prefer previous error. 125 } 126 } 127 } 128 private void removeConfig(Configuration configuration) throws Exception { 129 Class<?> classConfig = configuration.getClass(); 130 clearMap(classConfig, configuration, "mappedStatements"); 131 clearMap(classConfig, configuration, "caches"); 132 clearMap(classConfig, configuration, "resultMaps"); 133 clearMap(classConfig, configuration, "parameterMaps"); 134 clearMap(classConfig, configuration, "keyGenerators"); 135 clearMap(classConfig, configuration, "sqlFragments"); 136 clearSet(classConfig, configuration, "loadedResources"); 137 } 138 private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception { 139 Field field = classConfig.getDeclaredField(fieldName); 140 field.setAccessible(true); 141 ((Map) field.get(configuration)).clear(); 142 } 143 private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception { 144 Field field = classConfig.getDeclaredField(fieldName); 145 field.setAccessible(true); 146 ((Set) field.get(configuration)).clear(); 147 } 148 public boolean isChanged() throws IOException { 149 boolean isChanged = false; 150 for (Resource resource : findResource()) { 151 String key = resource.getURI().toString(); 152 String value = getMd(resource); 153 if (!value.equals(mappers.get(key))) { 154 isChanged = true; 155 mappers.put(key, value); 156 } 157 } 158 return isChanged; 159 } 160 private Resource[] findResource() throws IOException { 161 return resourcePatternResolver.getResources(XML_RESOURCE_PATTERN); 162 } 163 private String getMd(Resource resource) throws IOException { 164 return new StringBuilder().append(resource.contentLength()).append("-").append(resource.lastModified()).toString(); 165 } 166 } 167 }