一般这种课题,首先以就要阅读一下官方提供的帮忙文档
实在是太多了,这里就不全部列出来了。主要罗列了几个概念,以及一些比较重要的语句。
抽象类,双亲委托,亲ClassLoader等。
通过官方提供的样例,我们基本就可以写一个简单的ClassLoader。
java.lang.ClassLoader
A class loader is an object that is responsible for loading classes.The class ClassLoader is an abstract class.
A typical strategy is to transform the name into a file name and then read a "class file" of that name from a file system.
The ClassLoader class uses a delegation model to search for classes and resources.
Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself.
A sample implementation is:
* <blockquote><pre>
* class NetworkClassLoader extends ClassLoader {
* String host;
* int port;
*
* public Class findClass(String name) {
* byte[] b = loadClassData(name);
* return defineClass(name, b, 0, b.length);
* }
*
* private byte[] loadClassData(String name) {
* // load the class data from the connection
* . . .
* }
* }
* </pre></blockquote>
package com.dangxk.tools.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author dangxk
*/
public class MyClassloader extends ClassLoader {
private final String fileSuffix = ".class";
private String classLocation;
public MyClassloader(String classLocation) {
super();
this.classLocation = classLocation;
}
public MyClassloader(ClassLoader parentClassLoader, String classLocation) {
super(parentClassLoader);
this.classLocation = classLocation;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = loadClassData(name);
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String name) {
InputStream is = null;
ByteArrayOutputStream baos = null;
byte[] data = null;
try {
is = new FileInputStream(new File(classLocation + name.replace(".", "\\") + fileSuffix));
baos = new ByteArrayOutputStream();
int r = 0;
while (-1 != (r = is.read())) {
baos.write(r);
}
is.close();
baos.close();
data = baos.toByteArray();
} catch (Exception ex) {
Logger.getLogger(MyClassloader.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
is.close();
baos.close();
} catch (IOException exx) {
Logger.getLogger(MyClassloader.class.getName()).log(Level.SEVERE, null, exx);
}
}
return data;
}
@Override
public String toString() {
return "This is MyClassLoader:" + this.getClass().getSimpleName();
}
public static void main(String[] args) throws Exception {
MyClassloader myCl = new MyClassloader(ClassLoader.getSystemClassLoader(), "D:\\NetBeansProjects\\tools\\target\\classes\\");
testClassLoader(myCl);
}
private static void testClassLoader(MyClassloader cl) throws Exception {
Class<?> clazz = cl.findClass("com.dangxk.tools.test.EditDistance");
System.out.println(clazz.getClassLoader().toString());
Object newObj = clazz.newInstance();
System.out.println(newObj.toString());
}
}
输出:
This is MyClassLoader:MyClassloader
com.dangxk.tools.test.EditDistance@7852e922
实现基类(ClassLoader)的 findClass,通过文件系统读入外部的Class文件,转换成byteArray,生成对应的类。
流程就是这样。
接下来我们简单看一下在实现代码过程中主要用到的几个方法的官方定义。
/**
* Finds the class with the specified <a href="#name">binary name</a>.
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass <tt>loadClass</tt>} method after checking the
* parent class loader for the requested class. The default implementation
* throws a <tt>ClassNotFoundException</tt>.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
这里的 binary name就是上面例子中的,com.dangxk.tools.test.EditDistance
后面调用 return defineClass(name, classData, 0, classData.length);
就可以把从class文件读入的byteArray转化成对应的Class了。
※ 通过上面这些步骤我们就完成了用 java.lang.ClassLoader 帮助我们实现自定义类加载器的功能。
接下来是一些方便自己理解的东西,大家如果没有兴趣可以无视,哈哈。
注意此时我们只是创建了类,并没有加载这个类。
可以通过给 com.dangxk.tools.test.EditDistance 追加静态代码块儿去确认这个观点。
/**
* Converts an array of bytes into an instance of class <tt>Class</tt>.
* Before the <tt>Class</tt> can be used it must be resolved.
*
* <p> This method assigns a default {@link java.security.ProtectionDomain
* <tt>ProtectionDomain</tt>} to the newly defined class. The
* <tt>ProtectionDomain</tt> is effectively granted the same set of
* permissions returned when {@link
* java.security.Policy#getPermissions(java.security.CodeSource)
* <tt>Policy.getPolicy().getPermissions(new CodeSource(null, null))</tt>}
* is invoked. The default domain is created on the first invocation of
* {@link #defineClass(String, byte[], int, int) <tt>defineClass</tt>},
* and re-used on subsequent invocations.
*
* <p> To assign a specific <tt>ProtectionDomain</tt> to the class, use
* the {@link #defineClass(String, byte[], int, int,
* java.security.ProtectionDomain) <tt>defineClass</tt>} method that takes a
* <tt>ProtectionDomain</tt> as one of its arguments. </p>
*
* @param name
* The expected <a href="#name">binary name</a> of the class, or
* <tt>null</tt> if not known
*
* @param b
* The bytes that make up the class data. The bytes in positions
* <tt>off</tt> through <tt>off+len-1</tt> should have the format
* of a valid class file as defined by
* <cite>The Java™ Virtual Machine Specification</cite>.
*
* @param off
* The start offset in <tt>b</tt> of the class data
*
* @param len
* The length of the class data
*
* @return The <tt>Class</tt> object that was created from the specified
* class data.
*
* @throws ClassFormatError
* If the data did not contain a valid class
*
* @throws IndexOutOfBoundsException
* If either <tt>off</tt> or <tt>len</tt> is negative, or if
* <tt>off+len</tt> is greater than <tt>b.length</tt>.
*
* @throws SecurityException
* If an attempt is made to add this class to a package that
* contains classes that were signed by a different set of
* certificates than this class (which is unsigned), or if
* <tt>name</tt> begins with "<tt>java.</tt>".
*
* @see #loadClass(String, boolean)
* @see #resolveClass(Class)
* @see java.security.CodeSource
* @see java.security.SecureClassLoader
*
* @since 1.1
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
哈哈,又是一大堆,defineClass继续王后追的话,最终调用的就是native方法。就接触到了C++的东西了。
private native Class<?> defineClass0(String name, byte[] b, int off, int len, ProtectionDomain pd);
private native Class<?> defineClass1(String name, byte[] b, int off, int len, ProtectionDomain pd, String source);
private native Class<?> defineClass2(String name, java.nio.ByteBuffer b, int off, int len, ProtectionDomain pd, String source);
追到这儿,大家应该就明白了。我们差不多到头了。剩下的就只能参考各种文档,加深一下理解。追代码的方式已经不能继续进行了。
结语:
总之,从自定义类加载器入手,进一步理解java类加载相关的周边知识。其实这些东西可能在我们平时开发的时候基本上不会用到,但是理解了这些东西就能更好的接近基地实现。挖深一步,就更自信一点儿。