最近本来打算研究分布式的东西的,看了一位大侠写的分布式系统,其中要实现把class传到客户端,然后在客户端实例化的工作,就去研究了一下ClassLoader的原理。
准备工作:http://download.java.net/openjdk/jdk6/
压缩包中含有 Windows, Linux, Solaris 平台 JRE 源代码、JVM 源代码,以及 JDK 中类库和工具的源代码。
有了源代码就可以查看源码分析了。
JVM自带的类加载器可以分为三类:Boot Start ClassLoader,Ext ClassLoader, App ClassLoader
1. Boot Start ClassLoader其实不是一个类,其实就是直接由JVM自身来加载,因此当类如果是Boot Start ClassLoader加载的话,就会返回null。
public static void getBootStartClass() {
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i]);
}
}
从运行结果可以看到加载的都是Java/jdk1.6.0_23/jre/lib/下面的jar包
2.Boot Start ClassLoader会加载sun.misc.Launcher类。其中Lancherer调用ExtClassLoader.getExtClassLoader();生成ExtClassLoader。可见下面源代码
public Launcher() {
// Create the extension class loader
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader");
}
// Now create the class loader to use to launch the application
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader");
}
// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);
// Finally, install a security manager if requested
String s = System.getProperty("java.security.manager");
if (s != null) {
SecurityManager sm = null;
if ("".equals(s) || "default".equals(s)) {
sm = new java.lang.SecurityManager();
} else {
try {
sm = (SecurityManager)loader.loadClass(s).newInstance();
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
} catch (ClassNotFoundException e) {
} catch (ClassCastException e) {
}
}
if (sm != null) {
System.setSecurityManager(sm);
} else {
throw new InternalError(
"Could not create SecurityManager: " + s);
}
}
}
接下来我们看一下ExtClassLoader 类的源代码,可以看到继承了URLClassLoader类,加载的路径是系统属性java.ext.dirs。打印出来为:jdk1.6.0_23\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
static class ExtClassLoader extends URLClassLoader {
private File[] dirs;
/**
* create an ExtClassLoader. The ExtClassLoader is created
* within a context that limits which files it can read
*/
public static ExtClassLoader getExtClassLoader() throws IOException
{
final File[] dirs = getExtDirs();
try {
// Prior implementations of this doPrivileged() block supplied
// aa synthesized ACC via a call to the private method
// ExtClassLoader.getContext().
return (ExtClassLoader) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws IOException {
int len = dirs.length;
for (int i = 0; i < len; i++) {
MetaIndex.registerDirectory(dirs[i]);
}
return new ExtClassLoader(dirs);
}
});
} catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException();
}
}
void addExtURL(URL url) {
super.addURL(url);
}
/*
* Creates a new ExtClassLoader for the specified directories.
*/
public ExtClassLoader(File[] dirs) throws IOException {
super(getExtURLs(dirs), null, factory);
this.dirs = dirs;
}
private static File[] getExtDirs() {
String s = System.getProperty("java.ext.dirs");
File[] dirs;
if (s != null) {
StringTokenizer st =
new StringTokenizer(s, File.pathSeparator);
int count = st.countTokens();
dirs = new File[count];
for (int i = 0; i < count; i++) {
dirs[i] = new File(st.nextToken());
}
} else {
dirs = new File[0];
}
return dirs;
}
}
再看看APP ClassLoader,也继承了URLClassLoader。加载的路径是系统属性java.ext.dirs
static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader extcl)
throws IOException
{
final String s = System.getProperty("java.class.path");
final File[] path = (s == null) ? new File[0] : getClassPath(s);
// Note: on bugid 4256530
// Prior implementations of this doPrivileged() block supplied
// a rather restrictive ACC via a call to the private method
// AppClassLoader.getContext(). This proved overly restrictive
// when loading classes. Specifically it prevent
// accessClassInPackage.sun.* grants from being honored.
//
return (AppClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL[] urls =
(s == null) ? new URL[0] : pathToURLs(path);
return new AppClassLoader(urls, extcl);
}
});
}
}
既然有那么多加载器,那么它们的加载顺序是怎么样的呢?从下面的代码可以看到当加载类的时候,会先看一下当前类是不是已经被加载了,如果加载了就返回。如果没,就调用父类的加载器来加载类。如果父类的类加载器是null就调用bootsttap classloader来加载。这就是父类委托,因此在加载的时候是先从bootsttap classloader开始查找,一层层往下找。
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 invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
看了那么多源代码那么怎么实现自己的classloader呢,有什么作用呢,对于一些我们不想被反编译的类,我们可以调用加密技术将类加密。在使用的时候只要用自定义的classloader反编译就好了。
我们可以通过两种方法来写自己的classloader,下面分别来介绍:
第一种是继承ClassLoader方法主要是用FileInputStream读取class,然后用defineClass方法返回class。注意在defineClass()方法的第一个参数必须设置成null。不然会抛出异常
package com.fnk.classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class FileClassLoader extends ClassLoader {
private String directory = "C:/"; // Default directory.
private String type = ".class";
public FileClassLoader(String directory) {
this.directory = directory;
}
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data;
data = loadClassData(getFileName(name));
return defineClass(name, data, 0, data.length);
}
private String getFileName(String className){
int index = className.lastIndexOf(".");
if(index != -1){
return className.substring(index + 1);
}else{
return className;
}
}
private byte[] loadClassData(String name) throws ClassNotFoundException {
FileInputStream fis = null;
ByteArrayOutputStream baos = null;
try {
fis = new FileInputStream(directory + name + type);
baos = new ByteArrayOutputStream();
int len = 0;
while ((len = fis.read()) != -1) {
baos.write(len);
}
byte[] data = baos.toByteArray();
return data;
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
} finally {
if (fis != null)
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (baos != null)
try {
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
FileClassLoader classLoader1 = new FileClassLoader(
"./");
try {
Class c1 = classLoader1.loadClass("com.fnk.classloader.TestClass");
Testf tc1 = (Testf)c1.newInstance();
tc1.test();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第二种是 URLClassLoader 加载jar文件,然后调用loaderclass查找类。
package com.fnk.classloader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class MyURLClassLoader {
public static Class loaderClass(String file, String className) {
URL url;
Class myClass = null;
try {
url = new URL("file:" + file);
URLClassLoader myClassLoader = new URLClassLoader(
new URL[] { url }, Thread.currentThread()
.getContextClassLoader());
myClass = myClassLoader.loadClass(className);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return myClass;
}
public static void main(String[] args) {
try {
Testf testf = (Testf)loaderClass("./TestClass.jar",
"com.fnk.classloader.TestClass").newInstance();
testf.test();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
附件中是自定义的classloader