简体   繁体   English

解析Jar文件并找到类之间的关系?

[英]Parse Jar file and find relationships between classes?

How to detect whether the class from the jar file is extending other class or if there are method calls to other class objects or other class objects are created ? 如何检测jar文件中的类是否正在扩展其他类,或者是否创建了对其他类对象或其他类对象的方法调用? and then system out which class extend which class and which class called methods from which class . 然后系统输出哪个类扩展哪个类和哪个类从哪个类调用方法。

Im using Classparser to parser the jar . 我使用Classparser来解析jar。 here is part of my code : 这是我的代码的一部分:

        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();

Someone voted to close this question as "too broad". 有人投票将此问题视为“过于宽泛”。 I'm not sure whether this is the appropriate close reason here, but it might be, because one could consider this question (which is a follow up to your previous question ) as just asking others to do some work for you. 我不确定这是否是适当的接近原因,但可能是因为人们可以考虑这个问题(这是对你之前的问题的跟进),只是要求别人为你做一些工作。

However, to answer the basic question of how to detect references between classes in a single JAR file with BCEL : 但是,要回答如何使用BCEL检测单个JAR文件中的类之间的引用的基本问题:

You can obtain the list of JavaClass objects from the JarFile . 您可以从JarFile获取JavaClass对象的列表。 For each of these JavaClass objects, you can inspect the Method objects and their InstructionList . 对于每个JavaClass对象,您可以检查Method对象及其InstructionList Out of these instructions, you can select the InvokeInstruction objects and examine them further to find out which method on which class is actually invoked there. 在这些指令中,您可以选择InvokeInstruction对象并进一步检查它们以找出在那里实际调用哪个类的方法。

The following program opens a JAR file (for obvious reasons, it's the bcel-5.2.jar - you'll need it anyhow...) and processes it in the way described above. 以下程序打开一个JAR文件(出于显而易见的原因,它是bcel-5.2.jar - 你无论如何都需要它......)并以上述方式处理它。 For each JavaClass of the JAR file, it creates a map from all referenced JavaClass objects to the list of the Method s that are invoked on these classes, and prints the information accordingly: 对于JAR文件的每个JavaClass ,它将从所有引用的JavaClass对象创建一个映射到在这些类上调用的Method列表,并相应地打印信息:

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

Note, however, that this information might not be complete in every sense. 但请注意,此信息在各方面可能都不完整。 For example, due to polymorphism, you might not always detect that a method is called on an object of a certain class, because it is "hidden" behind the polymorphic abstraction. 例如,由于多态性,您可能无法始终检测到某个类的对象上调用了某个方法,因为它隐藏在多态抽象背后。 For example, in a code snippet like 例如,在代码片段中

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

the call to toString actually happens on an instance of MyClass . toString的调用实际上发生在MyClass一个实例上。 But due to polymorphism, it can only be recognized as a call to this method on "some Object ". 但是由于多态性,它只能被识别为对“some Object ”的这种方法的调用。

Disclaimer: This is, strictly speaking, not an answer to your question because it uses not BCEL but Javassist . 免责声明:严格来说,这不是您问题的答案,因为它不使用BCEL而是使用Javassist Nevertheless you may find my experiences and code useful. 不过,您可能会发现我的经验和代码很有用。


Few years ago I've written e Maven plugin (I called it Storyteller Maven Plugin ) for this very purpose - to analyse JARs files for dependencies which are unnecessary or nor required. 几年前我为此目的编写了e Maven插件(我称之为Storyteller Maven插件 ) - 分析JAR文件中的依赖项,这些依赖项是不必要的或不需要的。

Please see this question: 请看这个问题:

How to find unneccesary dependencies in a maven multi-project? 如何在maven多项目中找到不必要的依赖项?

And my answer to it. 我的回答

Although the plugin worked I have never released it back then. 虽然插件工作,但我从来没有发布它。 Now I've moved it to GitHub just to make it accessible for others. 现在我把它移到GitHub只是为了让别人可以访问它。

You ask about parsing a JAR to analyze the code in .class files. 您询问解析JAR以分析.class文件中的代码。 Below are a couple of Javassist code snippets. 下面是几个Javassist代码片段。

Search a JAR file for classes and create a CtClass per entry : 在JAR文件中搜索类,并为每个条目创建一个CtClass

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
            }
        }
        // ...
    }
}

Finding out referenced classes is then just : 找出引用的类只是

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

Overall my experience with Javassist for the very similar task was very positive. 总的来说,我对Javassist的非常相似的任务经验非常积极。 I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM