springmvc+mybatis
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.springframework.beans.BeansException; 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.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.FileSystems; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.Map; import java.util.Set; /** * 修改mapper文件不重启 */ public class MapperChangeListener implements InitializingBean, ApplicationContextAware { private volatile ConfigurableApplicationContext context = null; private volatile Scanner scanner = null; //mapper目录 private String mapperDir = "D:\\code\\xxx\\src\\main\\resources\\mapper"; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = (ConfigurableApplicationContext) applicationContext; } @Override public void afterPropertiesSet() throws Exception { scanner = new Scanner(); //新开辟一个线程监控mapper文件是否修改,新开辟线程是为了防止 watcher.take() 阻塞程序启动 new Thread(new watchMapper()).start(); } @SuppressWarnings("unchecked") class Scanner { //mapper资源目录,mapper文件在jar包中,监控不方便,不采用此种方式 //private static final String XML_RESOURCE_PATTERN = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "mapper/*.xml"; //框架 CommonEntityMapper.xml路径,因removeConfig 会清除Configuration中的CommonEntityMapper.xml 信息,故需重新加载 private static final String COMMONENTITYMAPPER_RESOURCE_PATTERN = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "xxx/CommonEntityMapper.xml"; private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); public Scanner(){} /** * 重新加载mapper * @throws Exception */ public void reloadXML() throws Exception { SqlSessionFactory factory = context.getBean(SqlSessionFactory.class); Configuration configuration = factory.getConfiguration(); removeConfig(configuration); for (Resource resource : findResource()) { try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } finally { ErrorContext.instance().reset(); } } for (Resource resource : findCommonEntityMapper()) { try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } finally { ErrorContext.instance().reset(); } } } /** * 移除Configuration中的mapper信息(mybatis的所有mapper信息保存在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"); } private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception { Field field = classConfig.getDeclaredField(fieldName); field.setAccessible(true); ((Map) field.get(configuration)).clear(); } private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception { Field field = classConfig.getDeclaredField(fieldName); field.setAccessible(true); ((Set) field.get(configuration)).clear(); } /** * 获取mapper文件 * @return * @throws IOException */ private Resource[] findResource() throws IOException { File f = new File(mapperDir); File[] tempList = f.listFiles(); FileSystemResource[] arraySource = new FileSystemResource[tempList.length]; for(int i=0; i<tempList.length; i++){ arraySource[i] = new FileSystemResource(tempList[i]); } return arraySource; } /** * 获取框架CommonEntityMapper * @return * @throws IOException */ private Resource[] findCommonEntityMapper() throws IOException { return resourcePatternResolver.getResources(COMMONENTITYMAPPER_RESOURCE_PATTERN); } } /** * 监听mapper修改 */ class watchMapper implements Runnable{ @Override public void run() { WatchService watcher = null; try { //注册服务 watcher = FileSystems.getDefault().newWatchService(); } catch (IOException e) { e.printStackTrace(); } try { //只监听修改 Paths.get(mapperDir).register(watcher, // StandardWatchEventKinds.ENTRY_CREATE, // StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); } catch (IOException e) { e.printStackTrace(); } while (true) { WatchKey key = null; try { key = watcher.take(); } catch (InterruptedException e) { e.printStackTrace(); } //如果监控到mapper文件修改事件 poll出事件 for (WatchEvent<?> event: key.pollEvents()) { System.out.println(event.context() + " comes to " + event.kind()); if(event.context().toString().endsWith(".xml")){ System.out.println("reload mapper"); try { //有mapper文件修改,重新加载mapper文件 scanner.reloadXML(); } catch (Exception e) { e.printStackTrace(); } } } boolean valid = key.reset(); if (!valid) { break; } } } } }
<!--修改mapper文件不重启-->
<bean id="mapperChangeListener" class="com.jumore.jdt.web.listener.MapperChangeListener" />