ITPub博客

首页 > 应用开发 > IT综合 > 直播卖货系统实现XML文件的热加载

直播卖货系统实现XML文件的热加载

IT综合 作者:云豹科技晓彤 时间:2021-08-09 17:13:59 0 删除 编辑

前言

平时我们进行Spring项目开发的时候,经常需要因为XML的改变而重启直播卖货系统服务器,假如项目较大的时候,重启将会非常耗时。最近编写的webschool框架需要用到较多的XML文件进行配置,在这里,直播卖货系统实现了对XML文件的热加载。
效果

每次直播卖货系统修改xml文件后,Spring都会对xml进行重新读取,自动覆盖原来的bean。

实现思路

在Spring中建立一个线程不断监控我们导入的xml文件是否已修改,一旦修改,马上重载。
继承扩展XmlWebApplicationContext类

/**
 * declaration: 
 *      扩展Xbean包中的XmlWebApplication类,使之支持热加载。
 *
 * author wenkangqiang
 * date   2016年3月5日
 */
public class XmlRefreshWebApplicationContext extends XmlWebApplicationContext {
    public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
    private static String springClassPathPrefix = "classpath:";
    public void reload() {
        //重新获得web.xml文件中"contextConfigLocation"参数(被加载的bean目录)
        String location = this.getServletContext().getInitParameter(
                CONFIG_LOCATION_PARAM);
        //通过特定的符号,;\n对参数进行分割,得出各个的路径
        String[] locations = StringUtils.tokenizeToStringArray(location,
                CONFIG_LOCATION_DELIMITERS);
        //获得被修改过的location,然后往下再重新加载那些被修改过的xml文件
        List<String> moidifiedLocationList = getModifiedLocation(locations);
        if (moidifiedLocationList != null && moidifiedLocationList.size() != 0) {
            this.setConfigLocations(StringUtils
                    .toStringArray(moidifiedLocationList));
            doRefresh();
        }
    }
    /**
     * 鉴定资源文件(*.xml)是否已经发生更改
     * @param locations
     * @return
     */
    public List<String> getModifiedLocation(String[] locations) {
        List<String> refreshLocationList = new ArrayList<String>();
        List<NoCacheClassPathResource> actualResources = new ArrayList<NoCacheClassPathResource>();
        ConfigFileModifiedFactory fileModifiedFactory = ConfigFileModifiedFactory
                .getInstance();
        try {
            /*
             * 获得资源文件对象(Resource)
             */
            for (int i = 0; i < locations.length; i++) {
                Resource[] resources = ((ResourcePatternResolver) this)
                        .getResources(locations[i]);
                for (int j = 0; j < resources.length; j++) {
                    actualResources
                            .add((NoCacheClassPathResource) resources[j]);
                }
            }
            /*
             * 通过逐个比较资源的最近修改时间来判断文件是否已经更改
             */
            for (NoCacheClassPathResource resource : actualResources) {
                Long fileLastModified = fileModifiedFactory.get(resource
                        .getPath());
                if (fileLastModified != null
                        && fileLastModified != resource.lastModified()) {
                    //已经发生修改,就把路径写入refreshLocationList的名单中,稍后reload
                    refreshLocationList.add(springClassPathPrefix
                            + resource.getPath());
                }
                //更新所有资源文件的最近修改时间
                fileModifiedFactory.put(resource.getPath(), resource
                        .lastModified());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return refreshLocationList;
    }
    public void doRefresh() {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) this
                .getBeanFactory();
        try {
            this.loadBeanDefinitions(beanFactory);
            finishBeanFactoryInitialization(beanFactory);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    /**
     * 根据路径获得资源文件
     * (non-Javadoc)
     * @see org.springframework.core.io.DefaultResourceLoader#getResource(java.lang.String)
     */
    public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            return new NoCacheClassPathResource(location
                    .substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        } else {
            try {
                URL url = new URL(location);
                return new UrlResource(url);
            } catch (MalformedURLException ex) {
                return getResourceByPath(location);
            }
        }
    }
}

编写一个类,专门保存直播卖货系统的path 文件的最近的修改时间

/**
 * declaration: 
 *          专门用于保存资源文件path : 修改时间的工厂类,实质上是维护着一个map
 * author wenkangqiang
 * date   2016年3月5日
 */
public class ConfigFileModifiedFactory {
    private static ConfigFileModifiedFactory configFactory = new ConfigFileModifiedFactory();
    private Map<String, Long> fileModifiedMap = new HashMap<String, Long>();
    public static ConfigFileModifiedFactory getInstance() {
        return configFactory;
    }
    public void put(String key, long lastModified) {
        fileModifiedMap.put(key, lastModified);
    }
    public Long get(String key) {
        return fileModifiedMap.get(key);
    }
}

无缓存导入类:NoCacheClassPathResource.java

/**
 * declaration: 
 *      路径指定资源文件,通过路径去获得资源文件的流
 * author wenkangqiang
 * date   2016年3月5日
 */
public class NoCacheClassPathResource extends ClassPathResource {
    public NoCacheClassPathResource(String path, ClassLoader classLoader) {
        super(path, classLoader);
    }
    public InputStream getInputStream() throws IOException {
        InputStream is = null;
        is = this.getClassLoader().getResource(this.getPath()).openStream();
        return is;
    }
}

定义探测的线程

/**
 * declaration: 
 *      设计线程,保持对资源文件的不断探测
 * author wenkangqiang
 * date   2016年3月5日
 */
public class SpringBeanReloadJob implements Runnable {
    private static final Logger log = LogManager
            .getLogger(SpringBeanReloadJob.class);
    @Override
    public void run() {
        try{
            XmlRefreshWebApplicationContext applicationContext = (XmlRefreshWebApplicationContext)ContextLoader
                    .getCurrentWebApplicationContext();
            applicationContext.reload();
        } catch (Exception ex) {
            log.error("Sping重载配置失败", ex);
        }
    }
}

在Spring的ApplicationContext文件中定义线程

<!-- XML文件热加载      使用JDK ScheduledExecutorService的定时任务配置 -->
    <!-- ScheduledThreadPoolExecutor集成工厂 -->
    <bean class="org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean" lazy-init="false">
        <property name="continueScheduledExecutionAfterException" value="true" />
        <property name="scheduledExecutorTasks">
            <list>
                <ref bean="scheduledExecutorTasks" />
            </list>
        </property>
    </bean>
    <!-- Task的封装 -->
    <bean id="scheduledExecutorTasks" class="org.springframework.scheduling.concurrent.ScheduledExecutorTask">
        <property name="runnable" ref="executorJob" />
        <!-- 首次执行延期2秒 -->
        <property name="delay" value="10000" />
        <property name="period" value="2000" />
        <!-- 固定间隔,否则默认fixDelay会等到前一个任务完成后才开始计时. -->
        <property name="fixedRate" value="true" />
    </bean>
    <bean id="executorJob" class="com.cn.qpm.framework.springdynaload.SpringBeanReloadJob" />

总结

至此,基于Spring的直播卖货系统的XML热加载的代码已经全部贴出来了。原理并不难,不过要编写出来还需对Spring的扩展多作认识。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70002045/viewspace-2786048/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
音视频软件开发相关知识科普账号

注册时间:2021-06-11

  • 博文量
    163
  • 访问量
    60337