热加载部署在spring框架中比较常用,修改代码不用重启项目就能实现项目热部署,这里使用代码简单实现以下热加载的过程。
代码部分
一般情况下同一个类只能被类加载器加载一次,所以这里要自定义一个类加载器去继承ClassLoader,动态类重载也是如此。
public class LoadClassInfo extends ClassLoader{
private ClassLoader classLoader;
private String pack_name;
public LoadClassInfo(ClassLoader parent,String pack_name) {
super(parent);
this.classLoader = parent;
this.pack_name = pack_name;
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(!name.contains(pack_name))return super.loadClass(name);
try {
if(!name.matches("(.+)\\.(.+)\\.(.+)"))return null;
InputStream input = classLoader.getResourceAsStream(String.format("%s.class",name.replace(".","/")));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
System.out.println("name "+name);
return defineClass(name,classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
定义一个线程池去监控指定包下的class文件,一旦有class文件更新,就会重新加载相应的class文件
ScheduledThreadPoolExecutor scheduleds = new ScheduledThreadPoolExecutor(1);
ClassLoader classLoader;
String file_path = (classLoader = ClassLoader.getSystemClassLoader())
.getResource("com.imageutil".replace(".","/")).getPath()
.substring(1);
try {
Map<String,String> maps = Files.list(Paths.get(file_path))
.filter(x->x.getFileName().toString().endsWith(".class"))
.collect(Collectors.toMap(m->{
return m.toString();
},m->{
try {
return String.valueOf(Files.size(m));
} catch (IOException e) {
e.printStackTrace();
return null;
}
},(a,b)->a));
maps.forEach((m,n)->{
System.out.println(m+" : "+n);
});
scheduleds.scheduleAtFixedRate(()->{
try {
Path[] paths = Files.list(Paths.get(file_path)).toArray(Path[]::new);
for (Path string : paths) {
String class_path = string.toString();
String last_time = String.valueOf(Files.size(string));
if(Objects.equals(last_time,maps.get(class_path)))continue;
String pack_name = class_path.split("classes")[1].replace("\\", ".").substring(1);
String class_name = pack_name.substring(0, pack_name.lastIndexOf("."));
LoadClassInfo loadClassInfos = new LoadClassInfo(classLoader,"com.imageutil");
Class<?> classs = loadClassInfos.loadClass(class_name);
classs.newInstance();
maps.put(class_path,last_time);
}
} catch (Exception e) {
e.printStackTrace();
}
},1,1,TimeUnit.SECONDS);
} catch (IOException e) {
e.printStackTrace();
}
编写测试类
package com.imageutil;
import java.util.concurrent.TimeUnit;
public final class TestInfo {
public TestInfo(){
System.out.println("修改前");
}
}
修改后的代码
package com.imageutil;
import java.util.concurrent.TimeUnit;
public final class TestInfo {
public TestInfo(){
System.out.println("修改后...................");
}
}
控制台输出
线程池监控到了class文件的变化,重新加载了该类并实例化对象