2014-11-05 17 views
1

jar 파일의 클래스가 다른 클래스를 확장하는지 여부를 확인하거나 다른 클래스 객체에 대한 메소드 호출이 있거나 다른 클래스 객체가 작성되면 어떻게 감지합니까? 그런 다음 어떤 클래스가 어떤 클래스를 확장하고 어떤 클래스가 어떤 클래스의 메소드를 호출했는지 시스템 아웃합니다.Jar 파일을 구문 분석하고 클래스 간의 관계를 찾으십니까?

Classparser를 사용하여 jar를 파싱합니다. 여기 내 코드의 일부는 다음과 같습니다

 String jarfile = "C:\\Users\\OOOO\\Desktop\\Sample.Jar"; 

     jar = new JarFile(jarfile); 
     Enumeration<JarEntry> entries = jar.entries(); 
     while (entries.hasMoreElements()) { 
      JarEntry entry = entries.nextElement(); 
      if (!entry.getName().endsWith(".class")) { 
       continue; 
      } 

      ClassParser parser = new ClassParser(jarfile, entry.getName()); 
      JavaClass javaClass = parser.parse(); 

답변

2

누군가가 "너무 광범위"로이 질문을 닫습니다 투표했다. 이것이 적절한 가까운 이유인지는 모르겠지만 다른 사람들에게 당신을 위해 약간의 일을하도록 요청하는 것처럼이 질문을 고려할 수 있으므로 (your previous question까지의 후속 조치입니다).

당신은 JarFile에서 JavaClass 개체의 목록을 얻을 수 있습니다 그러나

은 BCEL와 하나의 JAR 파일에 클래스 사이의 참조를 감지하는 방법 의 기본적인 질문에 대답합니다. 이 각각의 JavaClass 개체에 대해 Method 개체와 해당 InstructionList을 검사 할 수 있습니다. 이 지침에서 InvokeInstruction 개체를 선택하고 더 검사하여 실제로 어떤 클래스의 메서드가 실제로 호출되는지 확인할 수 있습니다.

다음 프로그램은 JAR 파일을 엽니 다 (분명한 이유는 bcel-5.2.jar입니다 - 어쨌든 필요합니다 ...) 위에서 설명한 방식으로 처리합니다. JAR 파일의 각 JavaClass의 경우,이 클래스에서 호출하는 Method의 목록에 참조 된 모든 JavaClass 객체에서지도를 생성하고, 그에 따라 정보를 인쇄합니다, 그러나,

import java.io.IOException; 
import java.util.Arrays; 
import java.util.Enumeration; 
import java.util.LinkedHashMap; 
import java.util.LinkedHashSet; 
import java.util.Map; 
import java.util.Map.Entry; 
import java.util.Set; 
import java.util.jar.JarEntry; 
import java.util.jar.JarFile; 

import org.apache.bcel.classfile.ClassFormatException; 
import org.apache.bcel.classfile.ClassParser; 
import org.apache.bcel.classfile.ConstantPool; 
import org.apache.bcel.classfile.JavaClass; 
import org.apache.bcel.classfile.Method; 
import org.apache.bcel.generic.ConstantPoolGen; 
import org.apache.bcel.generic.Instruction; 
import org.apache.bcel.generic.InstructionHandle; 
import org.apache.bcel.generic.InstructionList; 
import org.apache.bcel.generic.InvokeInstruction; 
import org.apache.bcel.generic.MethodGen; 
import org.apache.bcel.generic.ObjectType; 
import org.apache.bcel.generic.ReferenceType; 
import org.apache.bcel.generic.Type; 

public class BCELRelationships 
{ 
    public static void main(String[] args) throws Exception 
    { 
     JarFile jarFile = null; 
     try 
     { 
      String jarName = "bcel-5.2.jar"; 
      jarFile = new JarFile(jarName); 
      findReferences(jarName, jarFile); 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 
     finally 
     { 
      if (jarFile != null) 
      { 
       try 
       { 
        jarFile.close(); 
       } 
       catch (IOException e) 
       { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    private static void findReferences(String jarName, JarFile jarFile) 
     throws ClassFormatException, IOException, ClassNotFoundException 
    { 
     Map<String, JavaClass> javaClasses = 
      collectJavaClasses(jarName, jarFile); 

     for (JavaClass javaClass : javaClasses.values()) 
     { 
      System.out.println("Class "+javaClass.getClassName()); 
      Map<JavaClass, Set<Method>> references = 
       computeReferences(javaClass, javaClasses); 
      for (Entry<JavaClass, Set<Method>> entry : references.entrySet()) 
      { 
       JavaClass referencedJavaClass = entry.getKey(); 
       Set<Method> methods = entry.getValue(); 
       System.out.println(
        " is referencing class "+ 
        referencedJavaClass.getClassName()+" by calling"); 
       for (Method method : methods) 
       { 
        System.out.println(
         "  "+method.getName()+" with arguments "+ 
         Arrays.toString(method.getArgumentTypes())); 
       } 
      } 
     } 
    } 

    private static Map<String, JavaClass> collectJavaClasses(
     String jarName, JarFile jarFile) 
      throws ClassFormatException, IOException 
    { 
     Map<String, JavaClass> javaClasses = 
      new LinkedHashMap<String, JavaClass>(); 
     Enumeration<JarEntry> entries = jarFile.entries(); 
     while (entries.hasMoreElements()) 
     { 
      JarEntry entry = entries.nextElement(); 
      if (!entry.getName().endsWith(".class")) 
      { 
       continue; 
      } 

      ClassParser parser = 
       new ClassParser(jarName, entry.getName()); 
      JavaClass javaClass = parser.parse(); 
      javaClasses.put(javaClass.getClassName(), javaClass); 
     } 
     return javaClasses; 
    } 

    public static Map<JavaClass, Set<Method>> computeReferences(
     JavaClass javaClass, Map<String, JavaClass> knownJavaClasses) 
      throws ClassNotFoundException 
    { 
     Map<JavaClass, Set<Method>> references = 
      new LinkedHashMap<JavaClass, Set<Method>>(); 
     ConstantPool cp = javaClass.getConstantPool(); 
     ConstantPoolGen cpg = new ConstantPoolGen(cp); 
     for (Method m : javaClass.getMethods()) 
     { 
      String fullClassName = javaClass.getClassName(); 
      String className = 
       fullClassName.substring(0, fullClassName.length()-6); 
      MethodGen mg = new MethodGen(m, className, cpg); 
      InstructionList il = mg.getInstructionList(); 
      if (il == null) 
      { 
       continue; 
      } 
      InstructionHandle[] ihs = il.getInstructionHandles(); 
      for(int i=0; i < ihs.length; i++) 
      { 
       InstructionHandle ih = ihs[i]; 
       Instruction instruction = ih.getInstruction(); 
       if (!(instruction instanceof InvokeInstruction)) 
       { 
        continue; 
       } 
       InvokeInstruction ii = (InvokeInstruction)instruction; 
       ReferenceType referenceType = ii.getReferenceType(cpg); 
       if (!(referenceType instanceof ObjectType)) 
       { 
        continue; 
       } 

       ObjectType objectType = (ObjectType)referenceType; 
       String referencedClassName = objectType.getClassName(); 
       JavaClass referencedJavaClass = 
        knownJavaClasses.get(referencedClassName); 
       if (referencedJavaClass == null) 
       { 
        continue; 
       } 

       String methodName = ii.getMethodName(cpg); 
       Type[] argumentTypes = ii.getArgumentTypes(cpg); 
       Method method = 
        findMethod(referencedJavaClass, methodName, argumentTypes); 
       Set<Method> methods = references.get(referencedJavaClass); 
       if (methods == null) 
       { 
        methods = new LinkedHashSet<Method>(); 
        references.put(referencedJavaClass, methods); 
       } 
       methods.add(method); 
      } 
     } 
     return references; 
    } 

    private static Method findMethod(
     JavaClass javaClass, String methodName, Type argumentTypes[]) 
      throws ClassNotFoundException 
    { 
     for (Method method : javaClass.getMethods()) 
     { 
      if (method.getName().equals(methodName)) 
      { 
       if (Arrays.equals(argumentTypes, method.getArgumentTypes())) 
       { 
        return method; 
       } 
      } 
     } 
     for (JavaClass superClass : javaClass.getSuperClasses()) 
     { 
      Method method = findMethod(superClass, methodName, argumentTypes); 
      if (method != null) 
      { 
       return method; 
      } 
     } 
     return null; 
    } 
} 

참고이 그 모든면에서 정보가 완전하지 않을 수 있습니다. 예를 들어, 다형성으로 인해 다형성 추상화 뒤에 "숨겨져"있기 때문에 메서드가 특정 클래스의 개체에서 호출 된 것을 항상 감지하지는 못할 수 있습니다. 예를 들어, 코드

void call() { 
    MyClass m = new MyClass(); 
    callToString(m); 
} 
void callToString(Object object) { 
    object.toString(); 
} 

toString에 대한 호출이 실제로 MyClass의 인스턴스에서 발생 같은 니펫을. 그러나 다형성으로 인해 "some Object"에 대해서만이 메소드 호출을 인식 할 수 있습니다.

1

면책 조항 :이 BCEL하지만 Javassist되지 사용하기 때문에 이것은 엄격하게 귀하의 질문이 아닌 대답을 말하는. 그럼에도 불구하고 내 경험과 코드가 유용 할 수 있습니다.


몇 년 전 내가 작성한 전자 메이븐 플러그인을 바로이 목적을 위해 (나는 그것을 Storyteller Maven Plugin라고도 함) - 불필요하거나도 필요하다 종속성 JAR 파일 파일을 분석 할 수 있습니다.

이 질문을 참조하십시오 : 그것에

How to find unneccesary dependencies in a maven multi-project?

그리고 my answer합니다.

플러그인이 작동했지만 필자는 이전에 릴리스하지 않았습니다. 이제 다른 사람들이 액세스 할 수 있도록하기 위해 moved it to GitHub을 사용했습니다.

.class 파일의 코드를 분석하기 위해 JAR을 구문 분석하는 방법에 대해 문의하십시오. 다음은 몇 가지 Javassist 코드 단편입니다.

Search a JAR file for classes and create a CtClass per entry

:

final JarFile artifactJarFile = new JarFile(artifactFile); 
final Enumeration<JarEntry> jarEntries = artifactJarFile 
     .entries(); 

while (jarEntries.hasMoreElements()) { 
    final JarEntry jarEntry = jarEntries.nextElement(); 

    if (jarEntry.getName().endsWith(".class")) { 
     InputStream is = null; 
     CtClass ctClass = null; 
     try { 
      is = artifactJarFile.getInputStream(jarEntry); 
      ctClass = classPool.makeClass(is); 
     } catch (IOException ioex1) { 
      throw new MojoExecutionException(
        "Could not load class from JAR entry [" 
          + artifactFile.getAbsolutePath() 
          + "/" + jarEntry.getName() + "]."); 
     } finally { 
      try { 
       if (is != null) 
        is.close(); 
      } catch (IOException ignored) { 
       // Ignore 
      } 
     } 
     // ... 
    } 
} 

이 참조 클래스 is then just 밖으로 찾기 : 매우 유사한 작업에와 Javassist와

final Collection<String> referencedClassNames = ctClass.getRefClasses(); 

전체 내 경험은 매우 긍정적이었다. 이게 도움이 되길 바란다.