当然不能写进去。你的数据库主键或者Unique key,就是完整的类名
比如com.xyz.Test$1,
然后你覆盖java.lang.ClassLoader#findClass(java.lang.String)方法protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] b = loadClassData(className);
return defineClass(className, b, 0, b.length);
}
其中loadClassData是自定义的,你去数据库里面根据完整的className读取blob即可
byte[] b = loadClassData(className);
return defineClass(className, b, 0, b.length);
}
现在的问题就出在defineClass(className, b, 0, b.length);这个里面,对于主类com.xyz.Test.calss和com.xyz.Test1.calss、com.xyz.Test2.calss我在存储的时候该怎么存储?读取的时候我用loadClassData又该怎么读取才能让defineClass(className, b, 0, b.length);不报错
com.xyz.Test
com.xyz.Test$1
com.xyz.Test$2
com.xyz.Test$2应该还是项目默认的包路径不会从数据库中读取Test$1和Test$2吧
com.xyz.Test
com.xyz.Test$1
com.xyz.Test$2
它们是3个类。你默认的类加载器,也是去3个地方加载分3次加载,比如下面的例子,运行的时候,Test$MyThread.class根本不会被加载,除非你调用了foo之后。
匿名类不能用static段,为了打印类加载器加载的时机,所以这里没用匿名类,但原理一样。如果一定要测试匿名类。可以在编译以后,手工删除Test$1.class,只要没有调用bar(还要先new个Test)才会报NoClassDefFoundError
public class Test { static {
System.out.println("Test.<clinit>");
} public static void main(String[] args) {
System.out.println("Test.main");
// foo();
// new Test().bar();
} public static void foo() {
new MyThread().start();
}
public void bar() {
new Thread() {
public void run() {
}
}.start();
}
static class MyThread extends Thread {
static {
System.out.println("Test$MyThread.<clinit>");
}
public void run() {
System.out.println("Test$MyThread.run");
}
}
}
}
只有调用了bar(还要先new个Test)才会报NoClassDefFoundError
当需要某个匿名类时Java会通过ClassLoader加载, 你只要返回正确的匿名类class即可.
为何非要统一写到一个class文件中去啊,该分开放就分开呗,数据库也不是处理不了。
public class MyLoader extends ClassLoader {
......
//namefile是本地class类路径,classname是项目所在的类全称,如果本地有最新的类则优先加载最新的类,如果没有则默认加载项目中的
public Class load(String namefile, String classname) throws ClassNotFoundException {
Class clasz = null;
if (namefile != null) { //首先会判断本地有没有class类文件
byte[] classData = loadClassData(namefile);//如果有则读取该类的内容
if (classData != null) {//如果获取的class内容不为空则加载该内容
clasz = defineClass(classname, classData, 0, classData.length);
}
if (clasz == null) {//如果没有则加载系统默认的类
clasz = findSystemClass(classname);
}
}
if (clasz == null) {//如果为空,依然加载系统默认的类
clasz = loadClass(classname);
if (clasz != null) {
System.out.println(clasz.getName() + " is load");
}
}
return clasz;
}
.......
public byte[] loadClassData(String name) {//从本地的class类文件读取内容
try {
// 下面是定制部分,通过某种方法获取字节码数据
System.out.print("\r\n截获:" + name + "!!! ");
if (name != null && name.indexOf(".jar") >= 0) {//暂时不支持jar包上传
return null;
} else if (name != null && name.trim().toLowerCase().indexOf(".class") >= 0) {//只支持后缀名为.class的文件
if (new File(name).exists()) {//如果该类文件存在则加载
System.out.println("在文件:" + name + "中发现该类,装入解密后的数据!");
File file = new File(name);
long len = file.length();
byte[] return_classData = new byte[(int) len];
FileInputStream fin = new FileInputStream(file); // 读取当前目录中C.class文件中的内容
fin.read(return_classData); // 给return_classData数据赋值
fin.close();
return return_classData;
}
}
System.out.println("在遍历所有的ClassPath后,均没有发现该类,返回Null");
return null;
} catch (Exception ex) {
System.out.println("发生异常!");
ex.printStackTrace();
return null;
}
}
public static void main(String args[]) {
MyLoader loader = new MyLoader();
String namefile = "C:/class/FrameTest.class";//本地的一个类文件
Class class1 = null;
Class class2 = null;
try {
class1 = loader.load(namefile, "cn.com.test.FrameTest");//加载本地c盘下的class类文件
class2 = Class.forName("cn.com.test.FrameTest");//加载项目默认的class
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] m = class1.getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
System.out.println(m[i].toString());//输出本地c盘下FrameTest类的所有方法
}
Method[] n = class2.getDeclaredMethods();
for (int i = 0; i < n.length; i++) {
System.out.println(n[i].toString());//输出项目编译后的FrameTest类的所有方法
}
}
上面测试是在本地C盘下的class文件,存入数据库原理是一样的,如果cn.com.test.FrameTest没有匿名类或者内部类,即一个java文件编译后生成的class文件如果只有一个则不会出现问题,因为读取的都是那一个类,我先拷贝class到C盘下然后在项目中修改FrameTest类的方法,则上面输出的二者是不一样的,如果编译后生成的class类文件有两个及以上则就会出现问题,比如FrameTest.java编译后可能有FrameTest.class、FrameTest$1.class和FrameTest$bc.class,假如我依然加载FrameTest.class则相当于FrameTest$1.class和FrameTest$bc.class这两个类的内容依然用的是之前项目中的,并不是最新修改的,假如我又想加载那两个子类loadClassData这个方法又该如何读取内容,我想要解决的就是一个java文件会生成多个class类的情况,请指点
为何非要统一写到一个class文件中去啊,该分开放就分开呗,数据库也不是处理不了。
分开倒是没问题,问题是分开后我用这段代码如何去加载有多个子类组成的类
byte[] classData = loadClassData(namefile);//如果有则读取该类的内容
if (classData != null) {//如果获取的class内容不为空则加载该内容
clasz = defineClass(classname, classData, 0, classData.length);
}
为何非要统一写到一个class文件中去啊,该分开放就分开呗,数据库也不是处理不了。
分开倒是没问题,问题是分开后我用这段代码如何去加载有多个子类组成的类
byte[] classData = loadClassData(namefile);//如果有则读取该类的内容
if (classData != null) {//如果获取的class内容不为空则加载该内容
clasz = defineClass(classname, classData, 0, classData.length);
}你呀,还是没搞清楚状况,什么时候加载Test$1.class ???
不是加载Test.class的时候,而是用到它的时候,自然会自己去调用用它的那个类所在的类加载器,去找Test$1.class的
Don't call us. We will call you.
为何非要统一写到一个class文件中去啊,该分开放就分开呗,数据库也不是处理不了。
分开倒是没问题,问题是分开后我用这段代码如何去加载有多个子类组成的类
byte[] classData = loadClassData(namefile);//如果有则读取该类的内容
if (classData != null) {//如果获取的class内容不为空则加载该内容
clasz = defineClass(classname, classData, 0, classData.length);
}你呀,还是没搞清楚状况,什么时候加载Test$1.class ???
不是加载Test.class的时候,而是用到它的时候,自然会自己去调用用它的那个类所在的类加载器,去找Test$1.class的
Don't call us. We will call you.
你的意思是我byte[] classData = loadClassData(namefile);//依然只读取主类,它会自动加载跟namefile这个同目录下的子类?
我跟你举个例子,我前几个月,刚刚写过一个基于httpclient的classloader,也涉及到版本更新。你的问题完全思路错了。热部署做的不是也不可能是现有classloader重新去加载,而是销毁一个classloader,重新用个性的classloader