2014-11-05 80 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的後續),只是要求其他人爲你做一些工作。

然而,要回答的的基本問題是如何檢測類之間的引用在一個JAR文件與BCEL

您可以獲取從JarFileJavaClass對象的列表。對於這些JavaClass對象中的每一個,可以檢查Method對象及其對象InstructionList。在這些說明中,您可以選擇InvokeInstruction對象並進一步檢查它們以找出哪個類在哪個類上實際被調用。

以下程序將打開一個JAR文件(出於顯而易見的原因,它是bcel-5.2.jar--無論如何您將需要它......)並按上述方式處理它。對於JAR文件的每個JavaClass,它創建的所有引用JavaClass對象添加到這些類調用Method S的列表中的地圖,並據此打印信息:但是

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一個實例。但是由於多態性,它只能被識別爲在「某些Object」上調用此方法。

1

聲明:嚴格來說,這不是您的問題的答案,因爲它不使用BCEL而是使用Javassist。不過,您可能會發現我的經驗和代碼很有用。


幾年前,我已經寫ËMaven插件(我把它叫做Storyteller Maven Plugin)用於這個目的 - 要分析的JAR文件的依賴性既沒有必要,也沒有或需要。

請參閱此問題:

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

而且my answer它。

雖然插件工作,我從來沒有發佈過它。現在我已經moved it to GitHub只是爲了讓其他人可以訪問。

你問了解析JAR來分析.class文件中的代碼。以下是幾個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

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

總的來說我用Javassist經驗非常相似的任務是非常積極的。我希望這有幫助。