对classloader有研究的兄台请赐教

darkula 2005-06-17 06:19:46
最近被classloader整郁闷了……
目的:实现扩展接口的所有业务子类的动态加载,在系统运行时避免频繁重起
现已实现对单一类的动态加载:

就是extends ClassLoader,然后自定义了一个方法
public Object getInstance(String className)
在最后define一下class就可以了

现在的问题是:
如果某一业务子类在静态初始化块中调用了自制的ClassLoader来加载指定类,将不能实现静态加载(不在同一命名空间里……)

我也试过
AppLoader loader = new AppLoader();
Thread.currentThread().setContextClassLoader(loader);

但结果依旧……好象对静态初始化块,JVM会默认使用系统的类装载器;

不知是否我创建ClassLoader时有问题……有兄台做过这类东西么?
...全文
284 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
z8913257 2005-07-26
  • 打赏
  • 举报
回复
我写了个程序如下(调用了fast_time的CryptoClassLoader类):


import java.lang.reflect.*;
import java.lang.*;
import java.io.*;


public class testReflect{
public static void main(String[] args){
try{
Class aaa = Class.forName("buffer");
Object o = aaa.newInstance();
Class[] params = new Class[1];
params[0] = Class.forName("java.lang.String");
Method m = aaa.getMethod("printsome", params);
m.invoke(o, new String[]{"bbbbbbbbbbbbbb"});


String newClass = "public class dynClass{"+"public void print1(String str1){" +
"System.out.println(str1);}" +
"public void print2(String str2){" +
"System.out.println(str2);" +
"System.out.println(str2);" +
"System.out.println(str2);}}";

BufferedWriter os = new BufferedWriter(new FileWriter("dynClass.java"));
os.write(newClass);
os.flush();
os.close();
Runtime runtime = Runtime.getRuntime();
runtime.exec("javac dynClass.java");

//*************//Thread.sleep(1000);

test();

File f1 = new File("dynClass.java");
File f2 = new File("dynClass.class");
f1.delete();
f2.delete();
System.out.println("delete files---------------------");
}
catch(Exception e){e.printStackTrace(System.err);}
}

private static void test(){
try{
Class[] params = new Class[1];
params[0] = Class.forName("java.lang.String");



CryptoClassLoader cryptoClassLoader = new CryptoClassLoader(0);

//Class testDyn = Class.forName("dynClass");
Class testDyn = cryptoClassLoader.loadClass("dynClass",false);

Object dynObject = testDyn.newInstance();
Method method1 = testDyn.getMethod("print1", params);
Method method2 = testDyn.getMethod("print2", params);
method1.invoke(dynObject, new String[]{"11111111111"});
System.out.println("------------------");
method2.invoke(dynObject, new String[]{"22222222222"});
}
catch(Exception e){e.printStackTrace(System.err);
File f1 = new File("dynClass.java");
File f2 = new File("dynClass.class");
f1.delete();
f2.delete();
System.out.println("delete files---------------------");
}
}
}


现在碰到两个问题,如果直接用Class.forName装载类,就会报classNotFound,只能用cryptoClassLoader.loadClass方法。其次,如果在*************处不加Thread.sleep,就会报classNotFound,而且sleep的时间太短了也不行,时间长到一定程度就可以保证load成功,在两者之间的话就要凭运气了,这怎么办,有没有什么办法知道编译成功了?
fitLion 2005-06-23
  • 打赏
  • 举报
回复
学习一下
fast_time 2005-06-23
  • 打赏
  • 举报
回复
参考一下
class CryptoClassLoader extends ClassLoader
{ public CryptoClassLoader(int k)
{ key = k;
}

protected synchronized Class loadClass(String name,
boolean resolve) throws ClassNotFoundException
{ // check if class already loaded
Class cl = (Class)classes.get(name);

if (cl == null) // new class
{ try
{ // check if system class
return findSystemClass(name);
}
catch (ClassNotFoundException e) {}
catch (NoClassDefFoundError e) {}

// load class bytes--details depend on class loader

byte[] classBytes = loadClassBytes(name);
if (classBytes == null)
throw new ClassNotFoundException(name);

cl = defineClass(name, classBytes,
0, classBytes.length);
if (cl == null)
throw new ClassNotFoundException(name);

classes.put(name, cl); // remember class
}

if (resolve) resolveClass(cl);

return cl;
}

private byte[] loadClassBytes(String name)
{ String cname = name.replace('.', '/') + ".caesar";
FileInputStream in = null;
try
{ in = new FileInputStream(cname);
ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
int ch;
while ((ch = in.read()) != -1)
{ byte b = (byte)(ch - key);
buffer.write(b);
}
in.close();
return buffer.toByteArray();
}
catch (IOException e)
{ if (in != null)
{ try { in.close(); } catch (IOException e2) { }
}
return null;
}
}

private Map classes = new HashMap();
private int key;
}
darkula 2005-06-23
  • 打赏
  • 举报
回复
谢谢楼上的理论,但.....好象什么问题也没解决的样子?
darkula 2005-06-23
  • 打赏
  • 举报
回复
大家再来顶顶吧 ^_^ 人人有分~~~~
还有这里也人人有分~~~不够我再散,轻易不散分的, 嘿嘿
http://community.csdn.net/Expert/topic/4093/4093704.xml?temp=9.104556E-02
darkula 2005-06-23
  • 打赏
  • 举报
回复
谢谢fast_time(fast_time) 的例子

这个我在inside JVM里看到过....

看这句
{ // check if system class
return findSystemClass(name);
}
这样做的后果还是使用SystemClassLoader去读的这个类;

但这并不影响动态载入;

经过这几天,我发现我把问题的起点搞错了...自定制ClassLoader只是为了满足特殊需要而做的,例如从异地动态载入类并调用,定制自己的安全策略;

从JVM1.2以后完全可以靠Class.forName()来实现动态载入的;

服务器上没有达到动态载入的效果,可能是因为哪里的调用问题....慢慢再找找看吧..

OnlyFor_love 2005-06-22
  • 打赏
  • 举报
回复
顶一下
harbor1981 2005-06-22
  • 打赏
  • 举报
回复
http://blog.csdn.net/harbor1981/services/trackbacks/399984.aspx
harbor1981 2005-06-22
  • 打赏
  • 举报
回复
照下面做就可以了
步骤一、拷贝下面这个类到你的工程,我写的,可以进一步优化
package dmis;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.JarURLConnection;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.util.jar.Attributes;
import java.io.IOException;
import javax.swing.*;

/**
* A class loader for loading jar files, both local and remote.
*/
class JarClassLoader extends URLClassLoader {
private URL url;

/**
* Creates a new JarClassLoader for the specified url.
*
* @param url the url of the jar file
*/
public JarClassLoader(URL url) {
super(new URL[] { url });
this.url = url;
}

/**
* Returns the name of the jar file main class, or null if
* no "Main-Class" manifest attributes was defined.
*/
public String getMainClassName() throws IOException {
URL u = new URL("jar", "", url + "!/");
JarURLConnection uc = (JarURLConnection)u.openConnection();
Attributes attr = uc.getMainAttributes();
return attr != null ? attr.getValue(Attributes.Name.MAIN_CLASS) : null;
}

/**
* Invokes the application in this jar file given the name of the
* main class and an array of arguments. The class must define a
* static method "main" which takes an array of String arguemtns
* and is of return type "void".
*
* @param name the name of the main class
* @param args the arguments for the application
* @exception ClassNotFoundException if the specified class could not
* be found
* @exception NoSuchMethodException if the specified class does not
* contain a "main" method
* @exception InvocationTargetException if the application raised an
* exception
*/
public void invokeClass(String name, String[] args)
throws ClassNotFoundException,
NoSuchMethodException,
InvocationTargetException
{
Class c = loadClass(name);
Method m = c.getMethod("main", new Class[] { args.getClass() });
m.setAccessible(true);
int mods = m.getModifiers();
if (m.getReturnType() != void.class || !Modifier.isStatic(mods) ||
!Modifier.isPublic(mods)) {
throw new NoSuchMethodException("main");
}
try {
m.invoke(null, new Object[] { args });
} catch (IllegalAccessException e) {
// This should not happen, as we have disabled access checks
}
}

public void invokeClass(String name)
throws ClassNotFoundException,
NoSuchMethodException,
InvocationTargetException
{
Class c=loadClass(name);
Method m=c.getMethod("main",null);
m.setAccessible(true);
try{
m.invoke(null,null);
}catch(IllegalAccessException e){
}
}

}

二、子模块(jar文件)写接口
即在子模块的main class中多一个方法:public static main(){//这里启动整个子模块}
然后把该子模块打包成jar文件(注意manifest文件中必须声明该jar 文件的main class)
三、动态调用子模块
JarClassLoader cl=new JarClassLoader(url);//url指向你的子模块即jar文件的路径
String name=cl.getMainClassName();
cl.invokeClass(name);//OK!
harbor1981 2005-06-22
  • 打赏
  • 举报
回复
刚做过,用URLClassloader,跟Eclipse一样,你只要把你的子模块打包成jar文件(manifest文件中说明mainclass),这样就可以实现动态加载了
cathy97 2005-06-22
  • 打赏
  • 举报
回复
JRE中由ClassLoader负责查找和加载程序引用到的类库,基础类库ClassLoader会到rt.jar中自动加载,其它的类库,ClassLoader在环境变量CLASSPATH指定的路径中搜索,按照先来先到的原则,放在CLASSPATH前面的类库先被搜到. 在Console执行java.exe xxx命令以后,根据java.exe的传递参数,选择加载Server版的jvm.dll还是Client版的jvm.dll,然后加载jvm.dll,把控制权交给jvm.dll。

接下来,jvm.dll进行初始化,分配内存等等动作,然后在CLASSPATH路径中寻找class,找到class以后,寻找class中的程序入口点Main函数,然后从Main函数执行程序,在执行过程中,使用ClassLoader动态加载一系列引用到的类。

所谓动态加载也就是说通过Class.forName()加载的,这样在程序运行过程中,JRE中已经保存了很多class的Class对象,这样不需要从新从class file中读取class字节码来创建class了。
darkula 2005-06-22
  • 打赏
  • 举报
回复
harbor1981(赶紧赚钱回家娶老婆)
你给我的地址打不开……

另外,你给的这个ClassLoader我找的时候也看到过,这是用反射得到JAR包里的类信息,然后进行动态实例化,调用;

如果你不需要从网上调用异地class文件的话,完全可以将这段代码改成Class.forName来处理;

大概你没明白我的意思,我的意思是想在某一程序中的某段代码做更改后,在不重启服务的情况下,动态将其加载上来;

现在已经快做出来了,一起参考下这两篇文章吧。。。
http://www.developer.com/java/other/article.php/10936_2248831_2
http://jakarta.apache.org/commons/logging/xref-test/org/apache/commons/logging/LoadTest.html#19
patty79 2005-06-21
  • 打赏
  • 举报
回复
up!
flyxxxxx 2005-06-21
  • 打赏
  • 举报
回复
Handler h = (Handler)Lab.map.get("1");
这一行指定了Lab类是用系统类装载器来装载
Thread.currentThread().setContextClassLoader(loader);
这一行无效果原因见下面的JDK文档:
public void setContextClassLoader(ClassLoader cl)
Sets the context ClassLoader for this Thread. The context ClassLoader can be set when a thread is created, and allows the creator of the thread to provide the appropriate class loader to code running in the thread when loading classes and resources.
JDK的文档说的是线程创建(不是运行)时来set类装载器。
当前线程已经处于运行状态不能修改它的类装载器(基于安全原因)。
awers 2005-06-21
  • 打赏
  • 举报
回复
恩恩,UP下
humanity 2005-06-21
  • 打赏
  • 举报
回复
太专业了, 俺看不懂.
<SUP><B>UP</B></SUP>
darkula 2005-06-21
  • 打赏
  • 举报
回复
恩,但我也曾经试过
Handler m = (Handler)loader.getReloadableClass("HandlerImpl");
这样子也是不行的;
getReloadableClass里定义了打开了类 -- >虚拟机的通道;
用这个方法得到一个非static{}初始化的对象是可以实现动态加载的,但在这里就不可以了;

这里是改过后的,可以实现动态装载的部分
public class HandlerExecute{

public static void main(String[] args)throws Exception{
while(true){
Thread.sleep(1*1500);
AppLoader loader = new AppLoader();
Handler h = (Handler)loader.getReloadableClass("HandlerImpl");
h.execute();
}
}
}

但很显然这不是我们想要的,总不能每次调用实例都要去初始化一次吧……


darkula 2005-06-20
  • 打赏
  • 举报
回复
我还是把代码贴出来吧,这样大家可以帮忙看看;


//所有子模块要实现的接口
public interface Handler{
public void execute();
}

//先模拟一个实现
public class HandlerImpl implements Handler{
public void execute(){
System.out.println("Handler executing..");
}

}

//自定制classloader,用来实现动态加载
import java.io.*;
import java.util.*;

public final class AppLoader extends ClassLoader{
public AppLoader(){
super(AppLoader.class.getClassLoader());
}

//得到一个可动态加载的实例
public Object getReloadableClass(String className)
throws ClassNotFoundException{

byte[] clazzByte = getByteByClassFile(className);

Class clazz = this.defineClass(className,clazzByte,0,clazzByte.length);

Object instance = null;
try{
instance = clazz.newInstance();
}catch(IllegalAccessException iiex){
iiex.printStackTrace();
}catch(InstantiationException inex){
inex.printStackTrace();
throw new RuntimeException(className+"cant instantiation");
}
return instance;
}

//将指定的class文件大小返回
private byte[] getByteByClassFile(String className){
byte[] bytes = null;
InputStream in = null;
try{
Class clazz = Class.forName(className);

File clazzFile = null;

//在classpath中查找指定的class文件
for(int i = 0;i < pathElements.length;i ++ ){
String name = className.replace('.',File.separatorChar)+".class";
String dir = pathElements[i];
clazzFile = new File(dir + File.separatorChar + name);
if(clazzFile.exists())
break;
}
in = new FileInputStream(clazzFile);

bytes = new byte[in.available()];
in.read(bytes);
}catch(ClassNotFoundException clsex){
throw new RuntimeException("[Not foud class file:" + className
+ " in classpath]");
}catch(FileNotFoundException fex){
throw new RuntimeException("[" + className + " not exist]");
}catch(IOException ioex){
ioex.printStackTrace();
}finally{
try{
in.close();
}catch(IOException ioex){
ioex.printStackTrace();
}
}
return bytes;
}
}

//实验动态加载的效果
public class Lab{
//存放所有子实现模块
public static Hashtable map;

static{
init();
}

public static void init(){
map = new Hashtable();

MOHandler m1 = getInstance("MOHandlerImpl");

map.put("1",m1);
}

private static MOHandler getInstance(String className){
AppLoader loader = new AppLoader();
MOHandler instance = null;
try{
instance = (Handler)loader.getReloadableClass(className);
}catch(ClassNotFoundException clsex){
clsex.printStackTrace();
}
return instance;
}
}

//开始执行,并设置线程ClassLoader
public class HandlerExecute{

public static void main(String[] args)throws Exception{
AppLoader loader = new AppLoader();
Thread.currentThread().setContextClassLoader(loader);
Handler h = (Handler)Lab.map.get("1");
while(true){
Thread.sleep(1*1500);
h.execute();
}
}
}


不知为什么,这样子做,无法达到动态加载的效果,但是如果不把init写在static{}中就可以,我想是因为静态加载是由JVM的默认ClassLoader装载的,即使在静态块中进行改变线程ClassLoader设置也无效??
那TOMCAT是如何做到这点的??我也看了些它的源码,它是在Bootstrap时先指定的每一个应用块的ClassLoader,我和它的做法类似,为什么我的静态初始化块中的代码就无法实现动态加载??;
……郁闷……
darkula 2005-06-20
  • 打赏
  • 举报
回复
就当散分贴了...混分的进吧...
oyljerry 2005-06-19
  • 打赏
  • 举报
回复
^_^ up
加载更多回复(4)

62,612

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧