java8使用asm解析方法调用链

createh53周前 (12-05)技术教程14

在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就可以解析出整个调用链

相关文章

java函数的返回值(二)

哈喽大家好,接下来来学习函数的返回值。函数是被设计为特定任务的代码块,在执行完特殊任务之后需要把任务的结果返回给我们。在上一个视频封装求和函数的时候,计算后的结果处理方式是在函数内部处理。但是如果只在...