自定义ClassLoader 简单例子

转载 2009年12月10日 16:15:00

转载自 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文件等),以满足你自己的口味:)

相关文章推荐

自定义ClassLoader的简单例子 - bitan????s java - CSDNBlog

导读: Test1.java: package test; import java.io.*; import java.lang.reflect.*; class ACla...

自定义classloader的使用

  • 2013年09月20日 13:13
  • 34KB
  • 下载

java注解简单讲解以及自定义注解例子

注解(Annotation) jdk5定义了4个标准的元注解。除了元注解,还有其它帮我们定义好的注解如@SuppressWarnings  当然我们也可以自定义注解 @Target, @Retenti...
  • zymx14
  • zymx14
  • 2017年02月19日 23:22
  • 861

jni协议+自定义控件简单例子

  • 2016年08月24日 11:22
  • 2.83MB
  • 下载

简单的例子了解自定义ViewGroup(一)

在Android中,控件可以分为ViewGroup控件与View控件。自定义View控件,我之前的文章已经说过。这次我们主要说一下自定义ViewGroup控件。Viewgroup是作为父控件可以包含多...
  • byhieg
  • byhieg
  • 2016年08月16日 00:41
  • 82

自定义隐式转换和显式转换c#简单例子

自定义隐式转换和显式转换c#简单例子 (出自朱朱家园http://blog.csdn.net/zhgl7688) 例子:对用户user中,用户名first name和last name进行转换成合成...

一个简单的自定义事件的例子

windows 应用程序是靠消息驱动的,在WinForm开发中经常会通过重写WndProc或DefWndProc方法来截获消息,以添加自己的处理。 当然我们也可以定义自己的消息,发送,然后截获处理。...

IOS:最简单一个使用自定义的TableViewCell例子

一直在忙,也没空搞别的,之前写的都弄丢了,觉得还是做个笔记比较好; 既然是最简单的,那么直接计重点; 1,在界面里拖入一个 TabVlew; 2 ViewControler .h 的代码要实现两...
  • intbird
  • intbird
  • 2015年03月05日 23:31
  • 489

自定义view简单例子

自定义view简单例子本文主要讲解View的绘制,主要重写view的onDraw方法。效果图如下: 自定义绘制View,就少不了画笔Paint,这里简单介绍一下Paint: paint.setA...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自定义ClassLoader 简单例子
举报原因:
原因补充:

(最多只允许输入30个字)