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; ilength; 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;
                }
            }
        }
    }
}


    


你可能感兴趣的:(WatchService 监控 mapper文件修改后不需重启)