2012-04-17 3 views
1

Я пишу игровой движок, в котором мне нужно проанализировать каждый класс, упомянутый в моей программе. Поскольку это игровой движок, он должен быть присоединен к проекту клиента как файл JAR. Изнутри этого JAR-файла я должен иметь возможность сканировать каждый класс, который используется клиентом.Как установить класс ClassLoader как классLoader JVM для загрузки ВСЕХ классов (включая классы jar)?

Поэтому я думал, что должен создать пользовательский ClassLoader! Переопределяя ClassLoader, я могу взглянуть на каждый класс, который используется при его загрузке.

Я начал немного поиграть с ClassLoaders. Вот что я сделал: (я украл этот загрузчик классов от JavaWorld просто играть с ним)

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; 
} 
} 

Тогда я решил проверить:


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: 

     [email protected] 
      should be equal to 
     [email protected] 
      and also to: 
     null 

     *** 

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

    */ 


} 
} 
  1. Создает пользовательский ClassLoader правильный подход к проблеме, описанной вверху?

  2. Почему запускается загрузчик системного класса? Как заставить JVM использовать MY classloader, для всех потоков? (не только первый поток, я хочу, чтобы каждый новый поток создавал для автоматического использования моего загрузчика классов)

  3. Я понимаю, что с помощью «системы делегирования» родительский ClassLoader сначала пытается загрузить класс; это может быть причиной того, что мой ClassLoader ничего не загружает. Если я прав, как отключить эту функцию? Как я могу загрузить загрузчик MY classloader?

+0

См. Ответы на [этот вопрос здесь] (http://stackoverflow.com/questions/10192453/java-classloader-change/10192692#10192692). –

ответ

2

Инструментовка апи бы естественным для того, что я думаю, что вы пытаетесь сделать

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

Это позволяет перехватывать классы, как они загружаются в JVM - нормально для изменения их, но вам дается массив байтов, чтобы играть, поэтому я не вижу причин, по которым вы не могли бы выполнить какой-либо требуемый анализ без фактической модификации.

Это позволит вам анализировать классы до их загрузки. Я не совсем понимаю, что именно вы пытаетесь достичь, но если вы сможете анализировать классы после их загрузки, вы можете просто открыть и проанализировать содержимое всех банок в пути к классам.

+0

Я посмотрел на ссылку, которую вы предоставили, и на Google Instrumentation, но в обоих случаях я не уверен, на что я смотрю ... как перехватить классы, когда они загружаются? Если бы вы могли указать мне на какие классы я должен переопределить/какие методы я бы назвал кратко, это будет оценено – user1299784

+0

Эта ссылка содержит отправную точку http://www.informit.com/guides/content.aspx?g=java&seqNum=589 – henry

+0

@henry Начальная точка ушла. – Stephan