Technical support
In ASM, ClassReader class: resolves the existing and provides methods for obtaining class information.
Through ClassReader, we can parse a class.
Processing flow
Determine whether a class implements an interface according to the following process:
- Loop to determine whether the target interface is achieved
- Recursively determine whether the parent interface has a target interface
- Recursively determine whether the parent class implements the target interface
Concrete realization
The specific implementation code is as follows:
package utils; import org.objectweb.asm.ClassReader; import java.io.IOException; import java.util.Set; /** * Determine whether a class implements the specified interface set * * @author pengpj * @date 2018/11/27 */ public class SpecifiedInterfaceImplementionChecked { /** * Determine whether the specified interface is implemented * * @param reader class reader * @param interfaceSet interface collection * @return check result */ public static boolean hasImplSpecifiedInterfaces(ClassReader reader, Set<String> interfaceSet) { if (isObject(reader.getClassName())) { return false; } try { if (containedTargetInterface(reader.getInterfaces(), interfaceSet)) { return true; } else { ClassReader parent = new ClassReader(reader.getSuperName()); return hasImplSpecifiedInterfaces(parent, interfaceSet); } } catch (IOException e) { return false; } } /** * Check that the current class is of Object type * * @param className class name * @return checked result */ private static boolean isObject(String className) { return "java/lang/Object".equals(className); } /** * Check whether the interface and its parent interface implement the target interface * * @param interfaceList Interface to be checked * @param interfaceSet Target interface * @return checked result * @throws IOException exp */ private static boolean containedTargetInterface(String[] interfaceList, Set<String> interfaceSet) throws IOException { for (String inter : interfaceList) { if (interfaceSet.contains(inter)) { return true; } else { ClassReader reader = new ClassReader(inter); if (containedTargetInterface(reader.getInterfaces(), interfaceSet)) { return true; } } } return false; } }
test case
Some test cases are as follows:
private static Set<String> set; static { if (set == null) { set = new HashSet<>(); } set.add("com/cvte/myou/apm/agent/utils/SpecifiedInterfaceImplCheckedTest$A"); } @Test public void hasImplSpecifiedInterfacesTest() throws IOException { Assert.assertEquals(true, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(B.class.getName()), set)); Assert.assertEquals(true, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(C.class.getName()), set)); } @Test public void hasImplSpecifiedInterfacesObjectTest() throws IOException { Assert.assertEquals(false, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(Object.class.getName()), set)); } @Test public void hasImplSpecifiedInterfacesInterfaceTest() throws IOException { Assert.assertEquals(true, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(D.class.getName()), set)); } interface A { void hello(); } class B implements A { @Override public void hello() { } } abstract class C implements A { } interface D extends A { }
summary
In ASM, ClassReader is provided to parse the existing classes, which enables us to operate on the classes without source code.