クラスローダメモその2

クラスローダを自作する場合

1. DirectoryClassLoader の作成

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class DirectoryClassLoader extends ClassLoader {
	private static final int BUFFER_SIZE = 1024;
	private final String targetDirectory;

	DirectoryClassLoader(String targetDirectory) {
		if (targetDirectory == null) {
			throw new IllegalArgumentException(
					"TargetDirectory should not be null.");
		}
		if (targetDirectory.equals("")) {
			throw new IllegalArgumentException(
					"TargetDirectory should not be blank.");
		}
		this.targetDirectory = targetDirectory;
	}

	protected Class findClass(String name) throws ClassNotFoundException {
		try {
			byte[] data = read(new File(targetDirectory, name + ".class"));
			return defineClass(name, data, 0, data.length);
		} catch (Throwable t) {
			throw new ClassNotFoundException(name, t);
		}
	}

	private static byte[] read(File file) throws IOException {
		InputStream in = null;
		try {
			in = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE);
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			byte[] buf = new byte[BUFFER_SIZE];
			for (int readBytes = in.read(buf); readBytes != -1; readBytes = in
					.read(buf)) {
				out.write(buf, 0, readBytes);
			}
			return out.toByteArray();
		} finally {
			if (in != null)
				in.close();
		}
	}
}

2. Helloクラスの作成

次に、4種類の Hello クラスを作成する。

C:\eclipse_workspace\seasar2\classloader\bin

public class Hello {
	public static void shout() {
		System.out.println("Hello SystemClassLoader : "
				+ Hello.class.getClassLoader());
	}
}
public class Hello {
	public static void shout() {
		System.out.println("Hello loader1 : " + Hello.class.getClassLoader());
	}
}
public class Hello {
	public static void shout() {
		System.out.println("Hello loader2 : " + Hello.class.getClassLoader());
	}
}
public class Hello {
	public static void shout() {
		System.out.println("Hello URLClassLoader : "
				+ Hello.class.getClassLoader());
	}
}

それぞれコンパイルし、生成された class ファイルを別々のディレクトリに置く。
ここでは例として、C:\eclipse_workspace\classloader\loader1\bin,C:\eclipse_workspace\seasar2\loader2\bin
,C:\eclipse_workspace\seasar2\loader2\bin,C:\eclipse_workspace\seasar2\urlclassloader\binに置く。
つまりC:\eclipse_workspace\classloader\loader1\binはclasspathが通っている状態

まあ、

んな感じってこと。

3. テストプログラムの作成と実行

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {
	public static void main(String[] args) {

		ClassLoader loader1 = new DirectoryClassLoader(
				"C:\\eclipse_workspace\\seasar2\\loader1\\bin");

		ClassLoader loader2 = new DirectoryClassLoader(
				"C:\\eclipse_workspace\\seasar2\\loader2\\bin");

		URL[] urls = new URL[1];
		try {
			urls[0] = new File(
					"C:\\eclipse_workspace\\seasar2\\urlclassloader\\bin")
					.toURI().toURL();
		} catch (MalformedURLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		ClassLoader urlClassLoader = URLClassLoader.newInstance(urls);

		try {
			Class.forName("Hello").getMethod("shout").invoke(null,
					new Object[0]);
			Class.forName("Hello", true, loader1).getMethod("shout").invoke(
					null, new Object[0]);
			Class.forName("Hello", true, loader2).getMethod("shout").invoke(
					null, new Object[0]);
			Class.forName("Hello", true, urlClassLoader).getMethod("shout")
					.invoke(null, new Object[0]);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

なお、C:\eclipse_workspace\seasar2\classloader\binには、Test.class,Hello.class,DirectoryClassLoader.classの3つができる。

実行結果

Hello SystemClassLoader : sun.misc.Launcher$AppClassLoader@11b86e7
Hello SystemClassLoader : sun.misc.Launcher$AppClassLoader@11b86e7
Hello SystemClassLoader : sun.misc.Launcher$AppClassLoader@11b86e7
Hello SystemClassLoader : sun.misc.Launcher$AppClassLoader@11b86e7

ClassLoaderやURLClassLoaderでそのままつくるとシステムクラスローダが親になるため、Helloクラスはすべて
C:\eclipse_workspace\seasar2\classloader\bin\Hello.classから読み込まれたものになる。

  • DirectoryClassLoaderのコンストラクタでsuper(null)
  • URLClassLoader.newInstance(urls, null)

とやって、ブートストラップクラスローダを親にすれば、
実行結果は下記のようになる。

Hello SystemClassLoader : sun.misc.Launcher$AppClassLoader@11b86e7
Hello loader1 : DirectoryClassLoader@42e816
Hello loader2 : DirectoryClassLoader@190d11
Hello URLClassLoader : java.net.FactoryURLClassLoader@de6ced

参考: