java8使用asm解析方法调用链
在java中有些情况下想要获取函数调用时原始方法链式过程,如:
1.Function<Student,?> f =Student::getAge; 获取为 age,
2.Function<Student,?> f = x->x.getMa().getMb().getMc();解析出整个调用链为 ma ,mb,mc,
可以使用asm反编译获取整个调用链,spring的asm 和apache的asm都可以,代码如下
1.只使用java8自带的Function<?,?>是无法实现,需要定义一个CpFunction
import java.io.Serializable;
import java.util.function.Function;
/**
* @author maodali
*/
public interface CpFunction<T,R> extends Function<T, R>, Serializable {
}
2.反编译方法如下
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.asm.ClassReader;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import java.io.InputStream;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author maodali
*/
@Slf4j
public class PropertyUtil {
private static final Map<CpFunction<?, ?>, List<String>> cache = new HashMap<>();
@SneakyThrows
public static List<String> property(CpFunction<?, ?> function) {
List<String> s = cache.get(function);
if (s != null) {
return s;
}
Method method = function.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);
String methodName = serializedLambda.getImplMethodName();
List<String> propertyNameList = new ArrayList<>();
if (methodName.startsWith("lambda#34;) || methodName.contains("#34;)) {
String implClass = serializedLambda.getImplClass().replace('/', '.');
Class<?> clazz = Class.forName(implClass, true, PropertyUtil.class.getClassLoader());
try (InputStream is = clazz.getResourceAsStream(clazz.getSimpleName() + ".class");) {
ClassReader classReader = new ClassReader(is);
classReader.accept(new ClassVisitor2(propertyNameList, methodName), ClassReader.EXPAND_FRAMES);
}
} else {
propertyNameList = List.of(propertyName(methodName));
}
cache.put(function, propertyNameList);
return propertyNameList;
}
public static String propertyFirst(CpFunction<?, ?> function){
return property(function).get(0);
}
private static String propertyName(String methodName) {
if (methodName.startsWith("get")) {
methodName = methodName.substring(3);
} else if (methodName.startsWith("is")) {
methodName = methodName.substring(2);
}
return methodName.substring(0, 1).toLowerCase() + methodName.substring(1);
}
private static class ClassVisitor2 extends ClassVisitor {
private final List<String> propertyNameList;
private final String lambdaMethodName;
public ClassVisitor2(List<String> propertyNameList, String lambdaMethodName) {
super(Opcodes.ASM9);
this.propertyNameList = propertyNameList;
this.lambdaMethodName = lambdaMethodName;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (name.equals(lambdaMethodName)) {
return new MethodVisitor2(propertyNameList);
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
private static class MethodVisitor2 extends MethodVisitor {
private final List<String> propertyNameList;
public MethodVisitor2(List<String> propertyNameList) {
super(Opcodes.ASM9);
this.propertyNameList = propertyNameList;
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
if (opcode == Opcodes.GETFIELD) {
propertyNameList.add(name);
}
super.visitFieldInsn(opcode, owner, name, descriptor);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
if (opcode == Opcodes.INVOKEVIRTUAL) {
propertyNameList.add(propertyName(name));
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
}
调用PropertyUtil.property就可以解析出整个调用链