1

I am writing a game engine in which I need to analyze every single class that is mentioned in my program. As this is a game engine, it is to be attached to a client's project as a JAR file. From within that JAR file I need to be able to scan every class that is being used by the client.

So I thought that I should create a custom ClassLoader! By overriding ClassLoader I can take a look at every class that is being used as it is loaded.

I started playing around a bit with ClassLoaders. Here is what I did: (I stole this classloader from JavaWorld just to play around with it)

public class SimpleClassLoader extends ClassLoader {
private HashMap<String, Class> classes = new HashMap<String, Class>();

public SimpleClassLoader() {
}

/**
 * This sample function for reading class implementations reads
 * them from the local file system
 */
private byte getClassImplFromDataBase(String className)[] {
    System.out.println("        >>>>>> Fetching the implementation of "+className);
    byte result[];
    try {
        FileInputStream fi = new FileInputStream("store\\"+className+".impl");
        result = new byte[fi.available()];
        fi.read(result);
        return result;
    } catch (Exception e) {

        /*
         * If we caught an exception, either the class wasnt found or it
         * was unreadable by our process.
         */
        return null;
    }
}

/**
 * This is a simple version for external clients since they
 * will always want the class resolved before it is returned
 * to them.
 */
public Class loadClass(String className) throws ClassNotFoundException {
    return (loadClass(className, true));
}

/**
 * This is the required version of loadClass which is called
 * both from loadClass above and from the internal function
 * FindClassFromClass.
 */
public synchronized Class loadClass(String className, boolean resolveIt)
    throws ClassNotFoundException {
    Class result;
    byte  classData[];

    System.out.println(className);

    System.out.println("        >>>>>> Load class : "+className);

    /* Check our local cache of classes */
    result = (Class)classes.get(className);
    if (result != null) {
        System.out.println("        >>>>>> returning cached result.");
        return result;
    }

    /* Check with the primordial class loader */
    try {
        result = super.findSystemClass(className);
        System.out.println("        >>>>>> returning system class (in CLASSPATH).");
        return result;
    } catch (ClassNotFoundException e) {
        System.out.println("        >>>>>> Not a system class.");
    }

    /* Try to load it from our repository */
    classData = getClassImplFromDataBase(className);
    if (classData == null) {
        throw new ClassNotFoundException();
    }

    /* Define it (parse the class file) */
    result = defineClass(classData, 0, classData.length);
    if (result == null) {
        throw new ClassFormatError();
    }

    if (resolveIt) {
        resolveClass(result);
    }

    classes.put(className, result);
    System.out.println("        >>>>>> Returning newly loaded class.");
    return result;
}
}

Then I decided to test it:


public class Test
{
public static void main(String args[]) throws ClassNotFoundException
{
    SimpleClassLoader s = new SimpleClassLoader();
    Thread.currentThread().setContextClassLoader(s);

    Foo myfoo = new Foo();                          //local class
    ArrayList myList = new ArrayList();             //class from JAR file

    //both should have been loaded from my SimpleClassLoader
    System.out.println(s + "\n\tshould be equal to\n" + myfoo.getClass().getClassLoader());
    System.out.println("\tand also to: \n" + myList.getClass().getClassLoader());

    /*
       OUTPUT: 

       SimpleClassLoader@57fee6fc
            should be equal to
       sun.misc.Launcher$AppClassLoader@51f12c4e
            and also to: 
       null

       ***

       bizarre results: why are ArrayList and Foo not being loaded by my classloader?

     */


}
}
  1. Is creating a custom ClassLoader the correct approach to the problem described at the top?

  2. Why is the system class loader being invoked? How do I force the JVM to use MY classloader, for ALL threads? (not just the first thread, I want every new thread created to automatically use my classloader)

  3. It is my understanding that via the "delegation system", the parent ClassLoader gets the first try at loading the class; this may be the reason my ClassLoader isn't loading anything. If I am correct, how to I disable this feature? How do I get MY classloader to do the loading?

4

1 に答える 1

2

インストルメンテーションAPIは、あなたがやろうとしていると私が思うことに自然に適合します

http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html

クラスがJVMによってロードされるときにクラスをインターセプトできます-通常はそれらを変更しますが、操作するバイト配列が与えられるため、実際に変更せずに必要な分析を実行できない理由はわかりません。

これにより、ロードされる前にクラスを分析できます。何を達成しようとしているのかは完全にはわかりませんが、クラスがロードされた後にクラスを分析できれば、クラスパス上のすべての jar の内容を開いて解析するだけです。

于 2012-04-17T20:29:23.190 に答える