Java

カスタムクラスローダを作ってみる

More than 5 years have passed since last update.

ホットデプロイとかそういうのに興味が湧いたのでクラスローダとか触ってみます。

MyClassLoader.java

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

public class MyClassLoader extends ClassLoader {

    public static void main(String[] args) {
        for (String arg : args) {
            File classFile = new File(arg);
            String className = classFile.getName().replace(".class", "");
            MyClassLoader loader = new MyClassLoader(classFile, className);
            try {
                Class<?> clazz = Class.forName(className, true, loader);
                System.out.printf("load class: %s\n", clazz.getName());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    private final File file;
    private final String name;

    public MyClassLoader(File file, String name) {
        super();
        this.file = file;
        this.name = name;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (!name.equals(this.name)) {
            return super.findClass(name);
        }
        byte[] data = loadClassData();
        return defineClass(name, data, 0, data.length);
    }

    private byte[] loadClassData() {
        byte[] data = new byte[(int) file.length()];
        try {
            InputStream input = new BufferedInputStream(new FileInputStream(file));
            try {
                input.read(data);
            } finally {
                input.close();
            }
        } catch (FileNotFoundException e) {
            throw new IllegalArgumentException(e);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
        return data;
    }
}

とりあえずclassファイルのFileオブジェクトとクラス名を与えると、そのクラス名でクラスを読み込めるようになるクラスローダを作成。

MyClass.java

public class MyClass {
    static {
        System.out.println("loaded MyClass");
    }
}

ロードされると標準出力に文字を出力するだけのクラスを用意。

実行結果

$ javac *.java

両方ともコンパイルだけして

$ mkdir myclasses
$ mv MyClass.class myclasses

MyClass.classはクラスパスには入っていないディレクトリに移動。

$ java MyClassLoader myclasses/MyClass.class
loaded MyClass
load class: MyClass

引数で与えると、クラスパスには入っていないMyClass.classが読み込めている。

参考サイト