有没有办法判断 class 是否是接口?

Is there a way to tell if a class is an interface?

我正在尝试检查(在字节码级别,ASM)classes 实现一些特定的接口(在本例中,java.sql.Connection)并发现在某些情况下,库有另一个接口从我的接口集扩展了一些东西......然后他们的 classes 实现了那个接口。 (在这种情况下,一个新的扩展接口 com.mysql.jdbc.Connection 扩展 java.sql.Connection 然后是它们的实现,例如 ConnectionImpl 实现 com.mysql.jdbc.Connection。)因此我错过了将 ConnectionImpl 识别为目标 class。

所以.. 结果是我需要在加载 class 时将 com.mysql.jdbc.Connection 识别为 'interesting' 接口。但我看不出如何将 class 识别为接口与普通的 class。 ClassReader 中有什么东西可以给我那种信息吗?

您可以使用java.lang.ClassisInterface()方法来检查class是否是接口。

有一种方法可以帮你检查Class#isInterface()

if (yourClass.isInterface()) {
    //do something
}

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#isInterface%28%29

正如您已经提到的,您需要检查每个接口类型的接口以确定这样的子类型关系。但是,如果您可以访问所有资源,这并不难做到。

当您使用 ASM 时,您只需获取原始 class 的接口名称并找到每个此类接口的 class 文件。然后您可以解析每个 class 文件的接口等。这样,您就可以确定整个图并确定子类型关系。

如果您不想手动执行此操作,您可以使用 Byte Buddy,它为您提供类似于反射的方法 API 用于卸载类型:

ClassFileLocator cfl = ... // can be file system, class loader, etc.
TypePool.Default.of(cfl).describe("your.initial.type")
                        .resolve()
                        .isAssignableTo(someInterface);

如果目标接口不可用,您也可以使用TypePool读取目标接口。

根据标题,如果你想检查一个class是否是一个接口:

ClassNode node = // However you wish to load the class
if ((node.access & Opcodes.ACC_INTERFACE) != 0){
    // is interface
} else {
    // is not an interface
}

在您的 post 中,您声明您希望找到 java.sql.Connection 中的 children/implementations。

您遇到的问题是:
java.sql.Connection -> com.mysql.jdbc.Connection -> ConnectionImpl

ConnectionImpl 不直接实现 java.sql.Connection,因此未被检测为 child/implementation。对于这样的问题,您需要做的是遍历 class 层次结构。如果我处于您的情况,我会加载 ClassNodes <String, ClassNode> 的地图,其中字符串是 ClassNode 的名称。然后我会使用递归方法来检查给定的 class 是否是预期的类型。因此,如果您在其中发送一个 ClassNode,它将使用节点的 parent 调用自身,然后调用接口。最终,如果最初给定的节点是正确的类型,最终将通过并找到 java.sql.Connection 接口。此外,如果您想跳过冗余,您可以将每个 ClassNode 的结果存储在一个映射中,这样您就不必一遍又一遍地检查整个层次结构。

编辑:像这样

public static boolean isMatch(ClassNode cn, Map<String,ClassNode> nodes, String target){
    if (cn.name.equals(target)) return true;
    else{
        if (nodes.containsKey(cn.superName) && isMatch(nodes.get(cn.superName),nodes,target)) return true;
        for (String interf : cn.interfaces){
            if (nodes.containsKey(interf) && isMatch(nodes.get(interf),nodes,target)) return true;
        }
    }
    return false;
}

使用org.objectweb.asm.ClassReader,我们可以识别加载的class确实是一个class或接口。示例代码片段如下

  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException{

        ClassReader classReader = null;
        try{
            classReader=new ClassReader(classfileBuffer);
            if(isInterface(classReader)){
                return classfileBuffer;
            }
        }
        catch(Throwable exp){
            return classfileBuffer;
        }
       // Remaining logic here

    }

    public boolean isInterface(ClassReader cr) {
        return ((cr.getAccess() & 0x200) != 0);
    }