`

自定义ClassLoader 简单例子

    博客分类:
  • java
阅读更多

转载自 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小例子

    下面我们将详细讨论ClassLoader的基本概念、工作流程以及如何自定义ClassLoader。 1. **ClassLoader的基本概念** - 类加载器是Java中的一个核心组件,它负责将类的.class文件加载到JVM中,并转换为可执行的Java...

    定义ClassLoader调用外部jar包

    以下是一个简单的自定义ClassLoader示例,它能从指定的jar文件加载类: ```java public class MyClassLoader extends URLClassLoader { public MyClassLoader(String jarPath) throws MalformedURLException { ...

    classloader简单例子

    在这个简单的例子中,我们将探讨类加载器的基本概念、工作原理以及如何自定义类加载器。 类加载器的主要任务是查找并加载类的.class文件。Java中的类加载过程分为三个阶段:加载、验证、准备、解析和初始化。在我们...

    java自定义类加载classloader文档,包括代码

    本文将深入探讨Java中的类加载机制,并通过一个具体的自定义类加载器的例子来帮助理解其工作原理。 #### 二、Java类加载器的基本概念 Java中的类加载器主要负责完成以下三个基本任务: 1. **加载(Loading)**:...

    如何有效防止Java程序源码被人偷窥.doc

    Java 2引入的findClass方法使得创建自定义ClassLoader变得更加容易,我们只需要覆盖findClass,而无需完全实现loadClass,简化了过程。 自定义ClassLoader的工作原理是,首先创建一个ClassLoader的实例,然后使用这...

    关于java热部署知识.doc

    以下是一个简单的例子: ```java public class DynamicClassLoader extends ClassLoader { public Class<?> findClass(byte[] b) throws ClassNotFoundException { return defineClass(null, b, 0, b.length); }...

    classloader-playground, 一个简单的java依赖隔离容器类.zip

    "classloader-playground"项目的核心是一个简单的Java依赖隔离容器类。在实际开发中,我们常常需要隔离不同模块之间的依赖,避免版本冲突或循环依赖等问题。类加载器可以在这方面发挥重要作用,通过定制类加载器,...

    web_classloader_test.zip_web classloader

    `web_classloader_test`可能包含了这样的自定义类加载器的实现。 5. **热部署与热更新**:在开发阶段,开发者可能希望在不重启服务器的情况下更新代码。这就需要类加载器支持类的动态加载和卸载。这个测试可能包含...

    自定义类加载器

    本文将深入探讨自定义类加载器的基本概念、工作原理以及如何创建一个简单的基于磁盘的自定义类加载器。 ### 类加载器的层次结构 Java中的类加载器通常形成一个树状的层次结构,其中Bootstrap ClassLoader是最顶层...

    自定义类加载代码

    下面是一个简单的自定义类加载器示例: ```java public class MyClassLoader extends ClassLoader { public MyClassLoader(ClassLoader parent) { super(parent); } @Override protected Class<?> findClass...

    Java类动态加载(一)——java源文件动态编译为class文件

    动态加载类通常涉及自定义ClassLoader,这是因为它允许我们覆盖默认的加载行为,例如从网络、数据库或其他非标准位置加载类。 对于Java源文件动态编译,我们可以使用Java的内置工具`javac`或`javax.tools.Java...

    ClassLoader实例

    以下是一个简单的自定义类加载器示例,用于动态加载D盘下的A.class文件: ```java package jvm; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Method; import java.net.URL; ...

    Java虚拟机类装载:原理、实现与应用

    通过自定义ClassLoader,开发者可以实现更为复杂的应用架构,如插件系统、动态代码更新等,极大地提升了Java应用程序的可扩展性和维护性。在进行系统设计时,充分考虑类装载的原理和实现,可以帮助我们构建更加健壮...

    Java代码执行漏洞中类动态加载的应用1

    本文主要探讨了Java代码执行漏洞中类动态加载的应用,特别是通过自定义ClassLoader和反射调用`defineClass`方法来实现字节码的动态解析。 首先,Java中类的加载方式有两种:显式加载和隐式加载。隐式加载通常发生在...

    ClassLoader in OSGI

    下面通过一个小例子测试这三个JVM自带的ClassLoader之间的关系: ```java public class ClassLoaderTest1 { public static void main(String[] args) { ClassLoader loader = ClassLoaderTest1.class....

    java命令执行jar包的多种方法(四种方法)

    在使用自定义 Classloader 加载 JAR 包时,我们需要实现一个自定义的 Classloader 类,并在其中加载要加载的 JAR 包。这种方法比较复杂,需要专门的讨论。 以上四种方法都可以用来执行 JAR 包,但是它们有不同的...

    DexClassLoader自定义加载Assets目录下的dex、jar文件

    在这个例子中,“path_to_your_asset.dex”是位于Assets目录下的.dex文件路径,我们将其传递给DexClassLoader的构造函数。`getDir("dex", Context.MODE_PRIVATE)`用于创建一个私有的目录来存储优化后的.dex文件,...

Global site tag (gtag.js) - Google Analytics