有人投票決定關閉這個問題「太寬泛」。我不確定這是否是適當的緊密原因,但可能是因爲可以考慮這個問題(這是your previous question的後續),只是要求其他人爲你做一些工作。
然而,要回答的的基本問題是如何檢測類之間的引用在一個JAR文件與BCEL:
您可以獲取從JarFile
JavaClass
對象的列表。對於這些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
」上調用此方法。