自定义ClassLoader 简单例子

转载自 http://www.javaresearch.org/article/1007.htm

 

很多时候人们会使用一些自定义的ClassLoader ,而不是使用系统的Class Loader。大多数时候人们这样做的原因是,他们在编译时无法预知运行时会需要那些Class。特别是在那些appserver中,比如tomcat,Avalon-phonix,Jboss中。或是程序提供一些plug-in的功能,用户可以在程序编译好之后再添加自己的功能,比如ant, jxta-shell等。定制一个ClassLoader很简单,一般只需要理解很少的几个方法就可以完成。一个最简单的自定义的ClassLoader从ClassLoader类继承而来。这里我们要做一个可以在运行时指定路径,加载这个路径下的class的ClassLoader。通常我们使用ClassLoader.loadClass(String):Class方法,通过给出一个类名,就会得到一个相应的Class实例。因此只要小小的改动这个方法,就可以实现我们的愿望了。

 

源码: protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {

// First, check if the class has already been loaded

Class c = findLoadedClass(name);

 if (c == null) {

try { if (parent != null) {

 c = parent.loadClass(name, false);

 }else{ c = findBootstrapClass0(name);

 }

}catch(ClassNotFoundException e){

// If still not found, then call findClass in order

 // to find the class. c = findClass(name);

 }

 } if (resolve) { resolveClass(c);

}

 return c;

}

 

Source from ClassLoader.java First,check JavaAPI doc:上面指出了缺省的loadClass方法所做的几个步骤。 1. 调用findLoadedClass(String):Class 检查一下这个class是否已经被加载过了,由于JVM 规范规定ClassLoader可以cache它所加载的Class,因此如果一个class已经被加载过的话,直接从cache中获取即可。 2. 调用它的parent 的loadClass()方法,如果parent为空,这使用JVM内部的class loader(即著名的bootstrap classloader)。 3. 如果上面两步都没有找到,调用findClass(String)方法来查找并加载这个class。后面还有一句话,在Java 1.2版本以后,鼓励用户通过继承findClass(String)方法实现自己的class loader而不是继承loadClass(String)方法。既然如此,那么我们就先这么做:)

 

public class AnotherClassLoader extends ClassLoader {

private String baseDir;

private static final Logger LOG = Logger.getLogger(AnotherClassLoader.class);

public AnotherClassLoader (ClassLoader parent, String baseDir) {

super(parent); this.baseDir = baseDir;

}

protected Class findClass(String name) throws ClassNotFoundException {

 LOG.debug("findClass " + name);

 byte[] bytes = loadClassBytes(name);

Class theClass = defineClass(name, bytes, 0, bytes.length);//A

 if (theClass == null) throw new ClassFormatError();

return theClass;

}

 private byte[] loadClassBytes(String className) throws ClassNotFoundException {

try {

String classFile = getClassFile(className);

FileInputStream fis = new FileInputStream(classFile);

FileChannel fileC = fis.getChannel();

 ByteArrayOutputStream baos = new ByteArrayOutputStream();

 WritableByteChannel outC = Channels.newChannel(baos);

 ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

 while (true) { int i = fileC.read(buffer);

 if (i == 0 || i == -1) {

 break;

 }

buffer.flip();

outC.write(buffer);

buffer.clear();

 }

fis.close();

 return baos.toByteArray();

} catch (IOException fnfe) {

 throw new ClassNotFoundException(className);

 }

}

private String getClassFile(String name) {

StringBuffer sb = new StringBuffer(baseDir);

 name = name.replace('.', File.separatorChar) + ".class";

sb.append(File.separator + name); return sb.toString();

 }

}

 

[i]Ps:这里使用了一些JDK1.4的nio的代码:)[/i] 很简单的代码,关键的地方就在A处,我们使用了defineClass方法,目的在于把从class文件中得到的二进制数组转换为相应的Class实例。defineClass是一个native的方法,它替我们识别class文件格式,分析读取相应的数据结构,并生成一个class实例。 还没完呢,我们只是找到了发布在某个目录下的class,还有资源呢。我们有时会用Class.getResource():URL来获取相应的资源文件。如果仅仅使用上面的ClassLoader是找不到这个资源的,相应的返回值为null。 同样我们看一下原来的ClassLoader内部的结构。

 

public java.net.URL getResource(String name) {

 name = resolveName(name); ClassLoader cl = getClassLoader0();//这里

if (cl==null) {

// A system class. return ClassLoader.getSystemResource(name);

 }

return cl.getResource(name);

}

 

原来是使用加载这个class的那个classLoader获取得资源。

 

public URL getResource(String name) {

URL url;

 if (parent != null) {

url = parent.getResource(name);

} else { url = getBootstrapResource(name);

} if (url == null) {

url = findResource(name);//这里

}

return url;

}

 

这样看来只要继承findResource(String)方法就可以了。修改以下我们的代码:

 

//新增的一个findResource方法

protected URL findResource(String name) {

 LOG.debug("findResource " + name);

try { URL url = super.findResource(name);

 if (url != null) return url;

 url = new URL("file:///" + converName(name));

//简化处理,所有资源从文件系统中获取 return url;

} catch (MalformedURLException mue) {

LOG.error("findResource", mue); return null;

 }

}

private String converName(String name) {

StringBuffer sb = new StringBuffer(baseDir);

name = name.replace('.', File.separatorChar);

 sb.append(File.separator + name);

return sb.toString();

 }

 

好了,到这里一个简单的自定义的ClassLoader就做好了,你可以添加其他的调料(比如安全检查,修改class文件等),以满足你自己的口味:)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值