WatchService 监控 mapper文件修改后不需重启

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" />


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值