2012-08-23 2 views
4

내 계측 도구 용으로 특정 클래스를 계측 한 후 주 메서드를 시작하는 데 사용되는 래핑 ClassLoader를 제공하려고합니다. ClassLoader는 계측 된 특정 클래스의 버전을로드해야합니다. 그러나 Jetty와 JUnit의 경우 이러한 접근 방식은 자체적 인 클래스 로딩 계층 구조를 만들기 때문에 많이 제한됩니다.SystemClassLoader에 클래스를로드 할 때 클래스로드 순서를 어떻게 결정합니까?

VM 인수를 전달하고 싶지 않아 SystemClassLoader를 변경할 수 없습니다. 하지만 반성을 통해 ClassLoader.defineClass(String, byte[], int, int)을 공개하여 클래스와 강제로 연결할 수 있습니다.

ClassLoader scl = ClassLoader.getSystemClassLoader(); 
Method defineClass = ClassLoader.class.getDeclaredMethod(
     "defineClass", String.class, byte[].class, int.class, int.class); 
defineClass.setAccessible(true); 
for (String binaryName : classNamesToLoad) { 
    byte[] bytecode = this.declaredClasses.get(binaryName); 
    defineClass.invoke(scl, binaryName, bytecode, 0, bytecode.length); 
} 
defineClass.setAccessible(false); 

이 그냥 좋은 -하지만 왼쪽 한 가지 문제가있다 : 내 수업의 일부가 다른 클래스에서 상속이나 포함되어있는 경우, 그들은 올바른 순서로로드 할 수있는에 SystemClassLoader로드 때문에 현재에 의존하는 모든 클래스 - 계측되지 않은 버전을로드합니다.

A 
A.A extends B.A 
B 
B.A extends B.C 
B.C 

내가 원하는 경우 순서

B 
B.C 
B.A 
A 
A.A 

에로드되어야 할 것이다 : 여기

몇 가지 (가난라는 이름의) 클래스와 그들이 가지고있는 것 순서로로드 할과 예입니다 인스 트루먼 테이션 된 버전 만로드하십시오.

쉬운 방법이 있습니까? 예 : "setSystemClassLoader"메소드는 아직 발견하지 못했습니까?

SystemClassLoader를 조작 할 필요가없는 해결 방법은 무엇입니까?

또는 올바른 순서를 결정하기 위해로드하려는 클래스에서 전체 전이 의존성 분석을 실제로 수행해야합니까 (이 경우에는 어떤 "선행 기술"을 사용할 수 있습니까)?

감사합니다.

답변

1

전이 의존성 분석을 둘러 볼 방법이없는 것처럼 보입니다.

나는 이런 식으로 해결하고, 정말 사람이 구현에서 이익을 수 있기를 바랍니다 : 모든 (A DependencyDetector에, 당신은 add(ClassNode)을 부르는 ClassNode와 모든 종속성을 추가 :

import java.io.IOException; 
import java.util.ArrayDeque; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

import org.objectweb.asm.ClassReader; 
import org.objectweb.asm.tree.ClassNode; 

public class DependencyDetector { 

    private static class Node implements Comparable<Node> { 
     private final String binaryName; 
     private final Node[] imports; 
     private final int score; 

     private Node(String binaryName, Node...imports) { 
      this.binaryName = binaryName; 
      this.imports = imports; 
      this.score = calculateScore(imports); 
     } 

     public int compareTo(Node o) { 
      return score - o.score; 
     } 

     private int calculateScore(Node...imports) { 
      int newScore = 0; 
      for (Node n : imports) { 
       if (n.score >= newScore) { 
        newScore = n.score + 1; 
       } 
      } 
      return newScore; 
     } 
    } 

    private Map<String, Node> nodes = new HashMap<String, Node>(); 

    public DependencyDetector add(ClassNode node) { 
     Node n = nodes.get(node.name); 
     if (n == null) { 
      n = createNode(node); 
     } 
     return this; 
    } 

    private Node createNode(ClassNode node) { 
     String binaryName = node.name; 
     String[] importNames = extractImportedBinaryNames(node); 
     Node[] imports = new Node[importNames.length]; 
     for (int i = 0; i < imports.length; i++) { 
      String importName = importNames[i]; 
      Node imp = nodes.get(importName); 
      if (imp == null) { 
       ClassNode cn = new ClassNode(); 
       String path = importName.replace('.', '/') + ".class"; 
       try { 
        new ClassReader(
          ClassLoader.getSystemResourceAsStream(path) 
         ).accept(cn, ClassReader.SKIP_CODE); 
       } catch (IOException e) { 
        throw new RuntimeException(
         "could not read class " + importName); 
       } 
       imp = createNode(cn); 
       nodes.put(importName, imp); 
      } 
      imports[i] = imp; 
     } 
     Node result = new Node(binaryName, imports); 
     nodes.put(binaryName, result); 
     return result; 
    } 

    private String[] extractImportedBinaryNames(ClassNode node) { 
     String binaryName = node.name; 
     ArrayList<String> nodesToAdd = new ArrayList<String>(); 
     int endOfOuter = binaryName.lastIndexOf('$'); 
     if (endOfOuter >= 0) { 
      nodesToAdd.add(binaryName.substring(0, endOfOuter)); 
     } 
     if (node.superName != null) { 
      nodesToAdd.add(node.superName); 
     } 
     if (node.interfaces != null) { 
      for (String interf : (List<String>) node.interfaces) { 
       if (interf != null) { 
        nodesToAdd.add(interf); 
       } 
      } 
     } 
     return nodesToAdd.toArray(new String[nodesToAdd.size()]); 
    } 

    public String[] getClassesToLoad(String...binaryNames) { 
     String[] classNames = binaryNames != null && binaryNames.length > 0 
       ? binaryNames.clone() 
       : nodes.keySet().toArray(new String[nodes.size()]); 
     ArrayDeque<Node> dependencyQueue = new ArrayDeque<Node>(); 
     for (String className : classNames) { 
      Node node = nodes.get(className.replace('.', '/')); 
      dependencyQueue.add(node); 
      if (node == null) { 
       throw new RuntimeException(
        "Class " + className + " was not registered"); 
      } 
     } 
     HashMap<String, Node> dependencyMap = new HashMap<String, Node>(); 
     while (!dependencyQueue.isEmpty()) { 
      Node node = dependencyQueue.removeFirst(); 
      dependencyMap.put(node.binaryName, node); 
      for (Node i : node.imports) { 
       dependencyQueue.addLast(i); 
      } 
     } 
     ArrayList<Node> usedNodes = 
      new ArrayList<Node>(dependencyMap.values()); 
     Collections.sort(usedNodes); 
     String[] result = new String[usedNodes.size()]; 
     int i = 0; 
     for (Node n : usedNodes) { 
      result[i++] = n.binaryName.replace('/', '.'); 
     } 
     return result; 
    } 

    public boolean contains(String binaryName) { 
     return nodes.containsKey(binaryName.replace('.', '/')); 
    } 
} 

다음과 같이 사용되는 확장 또는 구현 또는 포함 된 클래스). 종속성 트리 작성을 완료하면 getClassesToLoad()을 호출하여 모든 종속성을 필요한 순서로 2 진 이름을 포함하는 String[]으로 검색합니다. 바이너리 이름을 getClassesToLoad(...)의 매개 변수로 지정하여 추가 된 모든 클래스와 그 종속성의 하위 집합을 요청할 수도 있습니다.

/** 
* load the specified classes (or all instrumented classes) 
* and all their dependencies with the specified ClassLoader. 
* @param loader 
* @param binaryNames binary names of all classes you want to load 
*  - none loads all instrumented classes 
*/ 
public void loadIntoClassLoader(ClassLoader loader, String...binaryNames) { 
    final String[] classNamesToLoad = 
     dependencies.getClassesToLoad(binaryNames); 
    Method defineClass = null; 
    Method findLoadedClass = null; 
    try { 
     // crack ClassLoader wide open and force-feed it with our classes 
     defineClass = ClassLoader.class.getDeclaredMethod(
       "defineClass", String.class, byte[].class, 
       int.class, int.class); 
     defineClass.setAccessible(true); 
     findLoadedClass = ClassLoader.class.getDeclaredMethod(
       "findLoadedClass", String.class); 
     findLoadedClass.setAccessible(true); 
     for (String binaryName : classNamesToLoad) { 
      if (!binaryName.startsWith("java.")) { 
       if (findLoadedClass.invoke(loader, binaryName) == null) { 
        byte[] bytecode = getBytecode(binaryName); 
        defineClass.invoke(loader, binaryName, bytecode, 
         0, bytecode.length); 
       } else if (declaredClasses.containsKey(binaryName)) { 
        throw new RuntimeException(
         "Class " + binaryName + " was already loaded, " + 
         "it must not be redeclared"); 
       } 
      } 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
     throw new RuntimeException(
      "could not load classes into ClassLoader", e); 
    } finally { 
     rehideMethod(findLoadedClass); 
     rehideMethod(defineClass); 
    } 
} 

private void rehideMethod(Method m) { 
    if (m != null) { 
     try { 
      m.setAccessible(false); 
     } catch (Exception e) { 
     } 
    } 
} 

private final DependencyDetector dependencies = new DependencyDetector(); 
private final Map<String, byte[]> declaredClasses = new HashMap<String, byte[]>(); 

private byte[] getBytecode(String binaryName) { 
    byte[] bytecode = declaredClasses.get(binaryName); 
    if (bytecode == null) { 
     // asBytes loads the class as byte[] 
     bytecode = 
      asBytes(binaryName.replace('.', '/') + ".class"); 
    } 
    return bytecode; 
} 
에 의존 : 나는 악기 수업, 나는 또한 DependencyDetectorClassNode를 추가하고이 같은 방법으로 그것을 전달하는 내가 필요한 모든 것을 검색 할 수 있습니다 지금

,

그것은 꽤 많이 있으며 지금까지 만난 모든 상황에서 훌륭하게 작동합니다.

0

인스턴스를 사용하여 개체를 확인하는 것은 클래스에 속하는지 여부입니다.

if (aAnimal instanceof Fish){ 
     Fish fish = (Fish)aAnimal; 
     fish.swim(); 
    } 
    else if (aAnimal instanceof Spider){ 
     Spider spider = (Spider)aAnimal; 
     spider.crawl(); 
    } 
+0

나는 그렇게 할 수 없다. instanceof 클래스 (클래스가 아닌 버전)를로드하고 더 이상 클래스 로더에서 내 버전을로드 할 수 없습니다. – Arne

관련 문제