springboot 打包之后无法动态加载问题解决
构建网络类加载器 继承ClassLoader 实现自定义类加载器
package com.net.utils;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
public class NetClassLoder extends ClassLoader {
private String url;
public NetClassLoder(String url){
this.url=url;
}
@Override
protected void finalize() throws Throwable {
System.out.println("类加载器回收了");
}
/**
*从网络中寻找到这个字节码 并且载入jvm
*/
@Override
public Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println(className);
Class<?> aClass;
byte[] classByNet = findClassByNet(className);
System.out.println(classByNet);
if(classByNet==null){
System.out.println("没找到");
// aClass = super.findClass(className);
// System.out.println("找到这个"+aClass);
return null;
}
aClass = defineClass(className,classByNet,0,classByNet.length);
return aClass;
}
protected byte[] findClassByNet(String className){
// 从网络上读取的类的字节
String path = pares2Url(className);
try {
URL url = new URL(path);
InputStream ins = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
// 读取类文件的字节
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (Exception e) {
return null;
}
}
private String pares2Url(String className) {
String replace = className.replace(".", "/");
return url+"/"+replace+".class";
}
}
//使用方式
String domain="http://172.0.10.173";
NetClassLoder classLoader = new NetClassLoder(domain);
Class<?> aClass = classLoader.findClass("com.load.plugin.impl.Command");
Cmd cmd = (Cmd) aClass.newInstance();
NetClass netClass = new NetClass();
cmd.test();
以上存在的问题发现
当netclassloder类加载器 被构建的时候
每次类加载的时候都会调用finalclass 去寻找
由于是网络中只有我们的目的class文件,导致其他字节码找不到
问题解决
重写在netclassLoader中重写 loadclass 方法
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
//优先从网络找字节码
Class<?> loadClass =findClass(name);
//第一情况 这个类 不需要由我们加载
//第二种情况 这个类需要由我们加载 但是 确实加载不到
if ( loadClass ==null){
//查看是否已经加载过 加载过就返回之前存在的
loadClass = findLoadedClass(name);
}
if(loadClass == null){
//如果网络和之前都没加载过 就调用系统的类加载器去加载这个类
loadClass= getSystemClassLoader().loadClass(name);
System.out.println(loadClass);
}
return loadClass;
}
//使用方式中修改为
String domain="http://172.0.10.173";
NetClassLoder classLoader = new NetClassLoder(domain);
Class<?> aClass = classLoader.loadClass("com.load.plugin.impl.Command");
Cmd cmd = (Cmd) aClass.newInstance();
NetClass netClass = new NetClass();
cmd.test();
以上idea 调试 和整合springboot 直接使用都没问题 但是打包部署却有问题
spring boot 类加载的时候是通过 启动类加载器 加载springboot/lib 下的包
所以使用系统的类加载器是无法识别到maven 依赖的,尽管已经打包到里面了
问题解决思路 获取到springboot的启动类加载器 优先从springboot中加载jar
完整代码
package com.net.utils;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
public class NetClassLoder extends ClassLoader {
private String url;
public NetClassLoder(String url){
this.url=url;
}
@Override
protected void finalize() throws Throwable {
System.out.println("类加载器回收了");
}
@Override
public Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println(className);
Class<?> aClass;
byte[] classByNet = findClassByNet(className);
System.out.println(classByNet);
if(classByNet==null){
System.out.println("没找到");
// aClass = super.findClass(className);
// System.out.println("找到这个"+aClass);
return null;
}
aClass = defineClass(className,classByNet,0,classByNet.length);
return aClass;
}
protected byte[] findClassByNet(String className){
// 从网络上读取的类的字节
String path = pares2Url(className);
try {
URL url = new URL(path);
InputStream ins = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
// 读取类文件的字节
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (Exception e) {
return null;
}
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> loadClass = findClass(name);
if ( loadClass ==null){
loadClass = findLoadedClass(name);
}
if(loadClass==null){
loadClass = Thread.currentThread().getContextClassLoader().loadClass(name);
System.out.println(loadClass);
}
return loadClass;
}
private String pares2Url(String className) {
String replace = className.replace(".", "/");
return url+"/"+replace+".class";
}
}
// 使用
String domain="http://172.0.10.173";
NetClassLoder classLoader = new NetClassLoder(domain);
Class<?> aClass = classLoader.loadClass("com.load.plugin.impl.Command");
Cmd cmd = (Cmd) aClass.newInstance();
NetClass netClass = new NetClass();
cmd.test();