软件行业流传着一幅漫画:开发软件就像制造小轿车,不是一开始就有设计图,也不是将轮子、车身、车门、发动机按部就班安装上去就可以的,而是大概先出现独轮车,接着出现自行车,然后是滑板车,之后是三轮自行车,继而是两轮摩托车··……
如此反复迭代,最后才得到成型的小轿车。这幅漫画讽刺的是开发新系统时“想当然”的做法,反映的是真实的探索过程。
其实,不仅开发系统是这样,即使是开发一个小工具,也遵循类似的原则,毕竟大部分软件被开发出来,是需要去解决实际中遇到的难题的。
n年前,小C曾入职一家软件公司,公司信息安全管理比较严格,员工办公电脑无法接入任何外部未授权设备。
在工作之中,也曾经做过一些技术预研,积累了一些代码。
公司有一个技术论坛,方便大家技术交流。论坛发帖采用后台审核机制。大家可以把自己遇到的问题以及代码发出来切磋。
对于这样的机制,少量的内容是合适的。一次偶然的机会发现论坛里面的短消息功能可以发送比较长的文本内容。
于是,问题便转变成了:如何把一个项目文件的内容快速的放到一个文本文件里面,并且接受到这个文本内容后如何快速恢复成项目原来的结构。
从软件需求的角度来说,这里面引申出2个基本需求:
因为今天,程序员其实是最具有开源精神的一类团体,有太多的内容和Idea需要通过code来交流。
虽然说有github、gitee等代码托管平台,但那是和文章是属于2套系统,而且代码托管平台上的代码是随时变动的。
于是便催生了发表文章的同时,能够方便将示例代码一同提交的需求,而且这个代码还需要反映出项目的结构,这跟上面的2个需求是不谋而合的!
运行1、2下载 procode-0.0.1.jar
运行3下载 procode-simple-0.0.1.jar
首先,选择需要备份的项目目录,并选择需要备份的文件类型
点击开始备份按钮,生成bak文件,成功后给出提示
备份成功后,提示是否查看备份文件
选择ok,则打开备份文件所在目录
切换到代码恢复tab,选择bak文件,以及恢复文件保存目录
点击开始恢复
恢复后的文件如图:
使用开发工具导入如下
参考 https://blog.csdn.net/qq_16127313/article/details/133792839
上面步骤演示生成的备份文件 spring-config-refresh_10241110.bak,大家可以自行验证文件恢复功能!
//goto pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fly</groupId>
<artifactId>spring-config-refresh</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
//goto src\main\java\com\fly\demo\Application.java
package com.fly.demo;
import java.io.IOException;
import java.net.URL;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.ResourceUtils;
import lombok.extern.slf4j.Slf4j;
/**
*
* SpringBoot 启动入口
*
* @author 00fly
* @version [版本号, 2018年7月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@EnableScheduling
@SpringBootApplication
public class Application implements CommandLineRunner
{
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args)
throws IOException
{
URL url = Application.class.getProtectionDomain().getCodeSource().getLocation();
log.info("Location URL Path: {}", url.getPath());
URL url2 = new ClassPathResource("cron").getURL();
log.info("url2 Path: {}", url2.getPath());
log.info("ResourceUtils.isFileURL: {}", ResourceUtils.isFileURL(url2));
log.info("ResourceUtils.isJarURL: {}", ResourceUtils.isJarURL(url2));
log.info("ResourceUtils.isJarFileURL: {}", ResourceUtils.isJarFileURL(url2));
}
}
//goto src\main\java\com\fly\demo\auto\job\ShowJob.java
package com.fly.demo.auto.job;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import com.fly.demo.auto.RedisConfig;
import com.fly.demo.auto.ReloadConfig;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
public class ShowJob
{
@Autowired
RedisConfig redisConfig;
@Autowired
ReloadConfig reloadConfig;
/**
* 默认线程池的大小为1,配置线程池支持多个定时任务线程并发执行
*
* @return
* @see [类、类#方法、类#成员]
*/
@Bean
ScheduledExecutorService scheduledExecutorService()
{
// return Executors.newScheduledThreadPool(8);
return new ScheduledThreadPoolExecutor(8, new CustomizableThreadFactory("schedule-pool-"));
}
@Scheduled(cron = "0/5 * 7-23 * * ?")
public void run()
{
reloadConfig.refresh(redisConfig);
log.info("redisConfig por={}", redisConfig.getPort());
}
}
//goto src\main\java\com\fly\demo\auto\RedisConfig.java
package com.fly.demo.auto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
*
* 配置文件值注入
*
* @author 00fly
* @version [版本号, 2018年7月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Configuration
@PropertySource(value = "classpath:redis.properties")
public class RedisConfig
{
@Value("${port}")
private int port;
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
}
//goto src\main\java\com\fly\demo\auto\ReloadConfig.java
package com.fly.demo.auto;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.springframework.context.annotation.Configuration;
import lombok.extern.slf4j.Slf4j;
/**
*
* 刷新配置文件
*
* @author 00fly
* @version [版本号, 2018年7月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@Configuration
public class ReloadConfig
{
private PropertiesConfiguration config;
public ReloadConfig()
{
super();
try
{
config = new PropertiesConfiguration("redis.properties");
FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();
strategy.setRefreshDelay(5000L);// 刷新周期5s
config.setReloadingStrategy(strategy);
}
catch (ConfigurationException e)
{
log.error("刷新配置数据出错", e);
}
}
/**
* 刷新配置数据到对象中
*
* @param redisConfig
* @see [类、类#方法、类#成员]
*/
public void refresh(RedisConfig redisConfig)
{
redisConfig.setPort(config.getInt("port"));
}
}
//goto src\main\java\com\fly\demo\auto\task\MyTask1.java
package com.fly.demo.auto.task;
import java.util.Date;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 定时任务类
*
* @author 00fly
* @version [版本号, 2017年4月25日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@Component
public class MyTask1 implements SchedulingConfigurer
{
PropertiesConfiguration config;
public MyTask1()
{
super();
try
{
config = new PropertiesConfiguration("demo.properties");
FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();
strategy.setRefreshDelay(60000L);// 刷新周期1分钟
config.setReloadingStrategy(strategy);
}
catch (ConfigurationException e)
{
log.error(e.getMessage(), e.getCause());
}
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
{
taskRegistrar.addTriggerTask(new Runnable()
{
@Override
public void run()
{
// 任务逻辑
log.info("★★★★★★★ MyTask1 run ★★★★★★★");
}
}, new Trigger()
{
@Override
public Date nextExecutionTime(TriggerContext triggerContext)
{
// 任务触发,可修改任务的执行周期
String cron = config.getString("schedule.myjob.cron");
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
});
}
}
//goto src\main\java\com\fly\demo\auto\task\MyTask2.java
package com.fly.demo.auto.task;
import java.util.Date;
import java.util.ResourceBundle;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 定时任务类
*
* @author 00fly
* @version [版本号, 2017年4月25日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@Component
public class MyTask2 implements SchedulingConfigurer
{
ResourceBundle config = ResourceBundle.getBundle("demo");
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
{
taskRegistrar.addTriggerTask(new Runnable()
{
@Override
public void run()
{
// 任务逻辑
log.info("------ MyTask2 run -------");
}
}, new Trigger()
{
@Override
public Date nextExecutionTime(TriggerContext triggerContext)
{
// 任务触发,ResourceBundle方式读取修改任务的执行周期
ResourceBundle.clearCache();
String cron = config.getString("schedule.myjob.cron");
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
});
}
}
//goto src\main\java\com\fly\demo\auto\task\MyTask3.java
package com.fly.demo.auto.task;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 定时任务类
*
* @author 00fly
* @version [版本号, 2017年4月25日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@Component
public class MyTask3 implements SchedulingConfigurer
{
Resource resource = new ClassPathResource("cron");
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
{
taskRegistrar.addTriggerTask(new Runnable()
{
@Override
public void run()
{
// 任务逻辑
log.info("######### MyTask3 run #########");
}
}, new Trigger()
{
@Override
public Date nextExecutionTime(TriggerContext triggerContext)
{
String cronText = "*/4 * * * * ?";
try
{
cronText = IOUtils.toString(resource.getURL(), StandardCharsets.UTF_8);
log.info("resource url = {}, cronText = {}", resource.getURL(), cronText);
}
catch (IOException e)
{
log.error(e.getMessage(), e.getCause());
}
// 任务触发,IO方式读取可修改任务的执行周期
return new CronTrigger(cronText).nextExecutionTime(triggerContext);
}
});
}
}
//goto src\main\resources\demo.properties
schedule.myjob.cron = */5 * * * * ?
//goto src\main\resources\redis.properties
port=8081