有没有办法判断 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.Class
的isInterface()
方法来检查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);
}
我正在尝试检查(在字节码级别,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.Class
的isInterface()
方法来检查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);
}