博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MyBatis的xml文件增量热加载,支持多数据源
阅读量:6712 次
发布时间:2019-06-25

本文共 9997 字,大约阅读时间需要 33 分钟。

  hot3.png

MyBatis热加载插件网上也看到过,但都是全部重新加载,这样必然效率不是很高,而且也不支持多数据源,所以还是自己实现吧!

package com.myapp.core;import java.io.IOException;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;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.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanFactoryUtils;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.core.io.Resource;/** * mapper.xml增量热加载,修改mapper.xml不需要重启tomcat等容器,正式环境去掉 */public class XMLMapperLoader implements DisposableBean, InitializingBean, ApplicationContextAware {    private Logger logger = LoggerFactory.getLogger(getClass());        private ApplicationContext applicationContext;    private ScheduledExecutorService executorService;    private Long initialDelay = 5L;    private Long period = 5L;    private boolean enabled = true;        public XMLMapperLoader(){        super();        logger.info(">>> XMLMapperLoader initialized...(MyBatis xml文件热部署模块初始完成,生产模式需要移除)");    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    public void afterPropertiesSet() throws Exception {        if(enabled){            Field field = org.mybatis.spring.SqlSessionFactoryBean.class.getDeclaredField("mapperLocations");            field.setAccessible(true);            Map
sqlSessionFactoryBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, org.mybatis.spring.SqlSessionFactoryBean.class); if(sqlSessionFactoryBeans != null && sqlSessionFactoryBeans.size() > 0){ executorService = Executors.newScheduledThreadPool(sqlSessionFactoryBeans.size()); for(org.mybatis.spring.SqlSessionFactoryBean sqlSessionFactoryBean : sqlSessionFactoryBeans.values()){ Resource[] mapperLocations = (Resource[]) field.get(sqlSessionFactoryBean); Scanner scanner = new Scanner(sqlSessionFactoryBean.getObject().getConfiguration(), mapperLocations); executorService.scheduleAtFixedRate(scanner, initialDelay, period, TimeUnit.SECONDS); } } } } public void setInitialDelay(Long initialDelay) { this.initialDelay = initialDelay; } public void setPeriod(Long period) { this.period = period; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public void setStrict(boolean strict) { this.strict = strict; } class Scanner implements Runnable { private Configuration configuration; private Resource[] mapperLocations; private HashMap
mapperFiles = new HashMap
(); private Set
loadedResources; private Map
knownMappers; private Collection
cacheNames; private Collection
mappedStatementNames; private Collection
parameterMapNames; private Collection
resultMapNames; private Collection
sqlFragmentNames; private Collection
keyGeneratorNames; public Scanner(Configuration configuration, Resource[] mapperLocations) { this.configuration = configuration; this.mapperLocations = mapperLocations; loadedResources = getSetField(Configuration.class, configuration, "loadedResources"); knownMappers = getMapField(MapperRegistry.class, configuration.getMapperRegistry(), "knownMappers"); cacheNames = configuration.getCacheNames(); mappedStatementNames = configuration.getMappedStatementNames(); parameterMapNames = configuration.getParameterMapNames(); resultMapNames = configuration.getResultMapNames(); keyGeneratorNames = configuration.getKeyGeneratorNames(); sqlFragmentNames = configuration.getSqlFragments().keySet(); this.scan(); } @Override public void run() { List
resources = this.getChangedXml(); if (resources != null && resources.size() > 0) { this.reloadXML(resources); } } public void reloadXML(List
resources){ for(Resource mapperLocation : resources){ refresh(mapperLocation); } } private void refresh(Resource resource){ try { XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(), new XMLMapperEntityResolver()); XNode context = xPathParser.evalNode("/mapper"); String namespace = context.getStringAttribute("namespace"); knownMappers.remove(Resources.classForName(namespace)); loadedResources.remove(resource.toString()); cacheNames.remove(namespace); cleanMappedStatements(context.evalNodes("select|insert|update"), namespace); cleanParameterMaps(context.evalNodes("/mapper/parameterMap"), namespace); cleanResultMaps(context.evalNodes("/mapper/resultMap"), namespace); cleanKeyGenerators(context.evalNodes("insert|update"), namespace); cleanSqlElements(context.evalNodes("/mapper/sql"), namespace); XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); System.err.println(">>> XML文件改变,重新加载\"namespace:" + namespace + "\"成功!"); } catch (Exception e) { logger.error("Refresh IOException :"+e.getMessage()); } finally { ErrorContext.instance().reset(); } } private Set
getSetField(Class
clazz, Object object, String fieldName){ try { Field setField = clazz.getDeclaredField(fieldName); setField.setAccessible(true); return (Set
) setField.get(object); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } private Map
getMapField(Class
clazz, Object object, String fieldName){ try { Field setField = clazz.getDeclaredField(fieldName); setField.setAccessible(true); return (Map
) setField.get(object); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } /** * 清理mappedStatements * * @param list * @param namespace */ private void cleanMappedStatements(List
list, String namespace) { for (XNode node : list) { String id = node.getStringAttribute("id"); mappedStatementNames.remove(namespace + "." + id ); } } /** * 清理parameterMap * * @param list * @param namespace */ private void cleanParameterMaps(List
list, String namespace) { for (XNode node : list) { String id = node.getStringAttribute("id"); parameterMapNames.remove(namespace + "." + id); } } /** * 清理resultMap * * @param list * @param namespace */ private void cleanResultMaps(List
list, String namespace) { for (XNode node : list) { String id = node.getStringAttribute("id", node.getValueBasedIdentifier()); resultMapNames.remove(id); resultMapNames.remove(namespace + "." + id); clearResultMaps(node, namespace); } } private void clearResultMaps(XNode xNode, String namespace) { for (XNode node : xNode.getChildren()) { if ("association".equals(node.getName()) || "collection".equals(node.getName()) || "case".equals(node.getName())) { if (node.getStringAttribute("select") == null) { resultMapNames.remove(node.getStringAttribute("id", node.getValueBasedIdentifier())); resultMapNames.remove(namespace + "." + node.getStringAttribute("id", node.getValueBasedIdentifier())); if (node.getChildren() != null && !node.getChildren().isEmpty()) { clearResultMaps(node, namespace); } } } } } /** * 清理selectKey * * @param list * @param namespace */ private void cleanKeyGenerators(List
list, String namespace) { for (XNode node : list) { String id = node.getStringAttribute("id"); keyGeneratorNames.remove(id + SelectKeyGenerator.SELECT_KEY_SUFFIX); keyGeneratorNames.remove(namespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX); } } /** * 清理sql节点缓存 * * @param list * @param namespace */ private void cleanSqlElements(List
list, String namespace) { for (XNode node : list) { String id = node.getStringAttribute("id"); sqlFragmentNames.remove(id); sqlFragmentNames.remove(namespace + "." + id); } } public void scan() { if (!mapperFiles.isEmpty()) { return; } for (Resource mapperLocation : this.mapperLocations) { String fileKey = getValue(mapperLocation); mapperFiles.put(mapperLocation.getFilename(), fileKey); } } private String getValue(Resource resource){ try { String contentLength = String.valueOf((resource.contentLength())); String lastModified = String.valueOf((resource.lastModified())); return new StringBuilder(contentLength).append(lastModified).toString(); } catch (IOException e) { throw new RuntimeException(e.getMessage(),e); } } public List
getChangedXml(){ List
resources = new ArrayList
(); for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } String name = mapperLocation.getFilename(); String value = mapperFiles.get(name); String fileKey = getValue(mapperLocation); if (!fileKey.equals(value)) { mapperFiles.put(name, fileKey); resources.add(mapperLocation); } } return resources; } } public void destroy() throws Exception { if (executorService != null) { executorService.shutdownNow(); } }}

使用时只需要将该插件纳入到Spring容器中即可。

可选配置参数说明:

  • enabled:是否启用该插件,默认值true
  • initialDelay:初始延迟多久后开始检测,单位秒,默认5s
  • period:间隔多久检测一次xml文件是否变化,单位秒,默认5s

 

转载于:https://my.oschina.net/houke/blog/901909

你可能感兴趣的文章
使用fastcgi_finish_request提高页面响应速度
查看>>
javascript成神之路(4):深入理解this关键字,是的就是this
查看>>
Flink1.4 Fault Tolerance源码解析-1
查看>>
Spring Cloud配置中心
查看>>
React 路由状态管理总结
查看>>
JAVA 几种引用类型学习
查看>>
Android WindowManager悬浮窗:不需要申请权限实现悬浮
查看>>
偶遇到客户的奇葩需求
查看>>
禅道 11.3 版本发布,主要完善细节,修复 bug
查看>>
无人机新用途,可精确识别危险海洋生物并向游泳者发出预警
查看>>
计算与推断思维 六、可视化
查看>>
8Manage装配式一体化管理如何解决集成窘境
查看>>
[翻译]Axure-Masters-原型设计工具Axure学习-第2.2节
查看>>
一文看清深圳云栖阿里云重磅产品发布
查看>>
ELK部署参考文档
查看>>
Dcloud中mui 微信支付和支付宝支付接口完美实现付款代码(PHP支付宝demo)
查看>>
逻辑思考之,指定轮次提交赛事结果验证是否合法
查看>>
Atomic
查看>>
对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)
查看>>
关于sql语句的优化问题
查看>>