反射是唯一方法调用的途径嘛?不妨看看MethodHandle(方法句柄)

闲逛的时候看见一篇文章

【雨夜】性能优化(反射调用)

本来,是去瞧瞧有什么优化的?毕竟平常反射是最常用的,而且用的很多,怎么还有更方便的方法?

没想到还真有是吧,好好好。

然后,我查查找找写了下面四个方法,都可以直接调用对象的get方法(不理解的东西可以点链接先了解,了解,嘿嘿嘿)

看完,又可以装13了,O(∩_∩)O哈哈~

方法一:通过SerializedLambda获取到方法信息,再使用MethodHandle拿到方法句柄执行方法

ini 复制代码
static Pattern RETURN_TYPE_PATTERN = Pattern.compile("^\(\)L(.*);");

public static <T> Object myInvokeGetter(T target, SerializableFunction<T, ?> func) {
    long l = System.currentTimeMillis();
    System.out.println("-->" + l);
    Object obj = null;
    try {
        SerializedLambda resolve = getSerializedLambda(func);
        String methodName = resolve.getImplMethodName();

        String expr = resolve.getImplMethodSignature();
        Matcher matcher = RETURN_TYPE_PATTERN.matcher(expr);
        if (!matcher.find() || matcher.groupCount() != 1) {
            throw new RuntimeException("获取Lambda信息失败");
        }
        String className = matcher.group(1).replace("/", ".");
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle virtual = lookup.findVirtual(target.getClass(), methodName, MethodType.methodType(Class.forName(className)));
        obj = virtual.invoke(target);
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("-->" + (System.currentTimeMillis() - l));
    return obj;
}

public static <T, R> SerializedLambda getSerializedLambda(Function<T, R> function) {
    SerializedLambda invoke = null;
    try {
        Method method = function.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(Boolean.TRUE);
        invoke = (SerializedLambda) method.invoke(function);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return invoke;
}
public static void main(String[] args) throws Throwable {
    OrderJackyun obj = new OrderJackyun();
    obj.setIsSalesAndOutAudit(1);
    Integer value1 = (Integer) HyReflectUtils.myInvokeGetter(obj,OrderJackyun::getIsSalesAndOutAudit);
}

方法二:同样是通过SerializedLambda获取到方法信息,不过是使用再使用LambdaMetafactory.metafactory(),方法结合MethodHandle、MethodType、Function执行方法

ini 复制代码
static Pattern RETURN_TYPE_PATTERN = Pattern.compile("^\(\)L(.*);");

public static <T> Object myInvokeGetter2(T target, SerializableFunction<T, ?> func) {
    long l = System.currentTimeMillis();
    System.out.println("-->" + l);
    Object apply = null;
    try {
        SerializedLambda resolve = getSerializedLambda(func);
        String methodName = resolve.getImplMethodName();

        String expr = resolve.getImplMethodSignature();
        Matcher matcher = RETURN_TYPE_PATTERN.matcher(expr);
        if (!matcher.find() || matcher.groupCount() != 1) {
            throw new RuntimeException("获取Lambda信息失败");
        }
        String className = matcher.group(1).replace("/", ".");
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        CallSite site = LambdaMetafactory.metafactory(
                lookup,
                "apply",
                MethodType.methodType(Function.class),
                MethodType.methodType(Object.class, Object.class),
                lookup.findVirtual(target.getClass(), methodName, MethodType.methodType(Class.forName(className))),
                MethodType.methodType(Class.forName(className), target.getClass()));
        Function function = (Function) ((CallSite) site).getTarget().invokeExact();
        apply = function.apply(target);
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("-->" + (System.currentTimeMillis() - l));
    return apply;
}

public static <T, R> SerializedLambda getSerializedLambda(Function<T, R> function) {
    SerializedLambda invoke = null;
    try {
        Method method = function.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(Boolean.TRUE);
        invoke = (SerializedLambda) method.invoke(function);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return invoke;
}

public static void main(String[] args) throws Throwable {
    OrderJackyun obj = new OrderJackyun();
    obj.setIsSalesAndOutAudit(1);
    Integer value1 = (Integer) HyReflectUtils.myInvokeGetter2(obj,OrderJackyun::getIsSalesAndOutAudit);
}

第三种:直接使用反射执行方法

ini 复制代码
public static <T> Object myInvokeGetter3(T target, String name) {
    long l = System.currentTimeMillis();
    System.out.println("-->" + l);
    Object invoke = null;
    try {
        Method method = target.getClass().getMethod("get" + StringUtils.capitalize(name));
        invoke = method.invoke(target);
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("-->" + (System.currentTimeMillis() - l));
    return invoke;
}
public static void main(String[] args) throws Throwable {
    OrderJackyun obj = new OrderJackyun();
    obj.setIsSalesAndOutAudit(1);
    Integer value1 = (Integer) HyReflectUtils.myInvokeGetter3(obj,"isSalesAndOutAudit");
}

第四种:hutool工具包提供的工具类,使用反射执行方法

ini 复制代码
public static <T> Object myInvokeGetter4(T target, String name) {
    long l = System.currentTimeMillis();
    System.out.println("-->" + l);
    Object invoke = ReflectUtil.invoke(target, "get" + StringUtils.capitalize(name));
    System.out.println("-->" + (System.currentTimeMillis() - l));
    return invoke;
}
public static void main(String[] args) throws Throwable {
    OrderJackyun obj = new OrderJackyun();
    obj.setIsSalesAndOutAudit(1);
    Integer value1 = (Integer) HyReflectUtils.myInvokeGetter4(obj,"isSalesAndOutAudit");
}

当然,方法有了,结果呢?(简单不严谨的测测)

第一和第二方法相差不大,大致都是3-9毫秒,一般维持在5-6左右

第三种差不多是上面两种时间的两倍,大致都是10-20毫秒,一般维持在10-15左右

第四种就比较久了,大致都是30-60毫秒,一般维持在50以上左右

但是不管是哪种方式都是第一次调用的时候耗时是这么久,连续的调用后面都是接近0毫秒

ini 复制代码
public static void main(String[] args) throws Throwable {
    OrderJackyun obj = new OrderJackyun();
    obj.setIsSalesAndOutAudit(1);
    Integer value1 = (Integer) HyReflectUtils.myInvokeGetter4(obj,"isSalesAndOutAudit");
    Integer value1 = (Integer) HyReflectUtils.myInvokeGetter4(obj,"isSalesAndOutAudit");
}

比如这种,第二次甚至多次(并不局限这个属性也可以是其他属性)都是1或者0毫秒

最后:hutool工具包因为是提供的公用方法,考虑和验证的地方比较多,会慢也是正常, 同时,去查看反射的invoke方法执行,你会发现到最后还是会通过MethodHandle去调用

invoke:源码

typescript 复制代码
public static <T> T invoke(Object obj, String methodName, Object... args) throws UtilException {
    Assert.notNull(obj, "Object to get method must be not null!", new Object[0]);
    Assert.notBlank(methodName, "Method name must be not blank!", new Object[0]);
    Method method = getMethodOfObj(obj, methodName, args);
    if (null == method) {
        throw new UtilException("No such method: [{}] from [{}]", new Object[]{methodName, obj.getClass()});
    } else {
        return invoke(obj, method, args);
    }
}

上面方法的下一步

ini 复制代码
public static <T> T invoke(Object obj, Method method, Object... args) throws UtilException {
    setAccessible(method);
    Class<?>[] parameterTypes = method.getParameterTypes();
    Object[] actualArgs = new Object[parameterTypes.length];
    if (null != args) {
        for(int i = 0; i < actualArgs.length; ++i) {
            if (i < args.length && null != args[i]) {
                if (args[i] instanceof NullWrapperBean) {
                    actualArgs[i] = null;
                } else if (!parameterTypes[i].isAssignableFrom(args[i].getClass())) {
                    Object targetValue = Convert.convert(parameterTypes[i], args[i]);
                    if (null != targetValue) {
                        actualArgs[i] = targetValue;
                    }
                } else {
                    actualArgs[i] = args[i];
                }
            } else {
                actualArgs[i] = ClassUtil.getDefaultValue(parameterTypes[i]);
            }
        }
    }

    if (method.isDefault()) {
        return MethodHandleUtil.invokeSpecial(obj, method, args);
    } else {
        try {
            return method.invoke(ClassUtil.isStatic(method) ? null : obj, actualArgs);
        } catch (Exception var7) {
            throw new UtilException(var7);
        }
    }
}

下一步,上面方法的MethodHandleUtil.invokeSpecial()方法执行源码,在这你就发现熟悉的MethodHandle以及lookup,只不过这里是直接通过方法获取对应的句柄。

ini 复制代码
public static <T> T invoke(boolean isSpecial, Object obj, Method method, Object... args) {
    Assert.notNull(method, "Method must be not null!", new Object[0]);
    Class<?> declaringClass = method.getDeclaringClass();
    Lookup lookup = lookup(declaringClass);

    try {
        MethodHandle handle = isSpecial ? lookup.unreflectSpecial(method, declaringClass) : lookup.unreflect(method);
        if (null != obj) {
            handle = handle.bindTo(obj);
        }

        return handle.invokeWithArguments(args);
    } catch (Throwable var7) {
        throw new UtilException(var7);
    }
}

感谢:

# 【雨夜】性能优化(反射调用)

装逼利器,看完这篇,让只懂反射的同学仰视你

# LambdaMetafactory替代反射,提高效率

jdk8的SerializedLambda方法解释

# Java中获取Lambda表达式的参数类型和返回值类型

# LambdaMetafactory类的用法以及使用场景

# MethodHandle(方法句柄)

MethodHandle结合LambdaMetafactory-使用方法及性能测试

相关推荐
他日若遂凌云志6 分钟前
深入剖析 Fantasy 框架的消息设计与序列化机制:协同架构下的高效转换与场景适配
后端
快手技术23 分钟前
快手Klear-Reasoner登顶8B模型榜首,GPPO算法双效强化稳定性与探索能力!
后端
二闹32 分钟前
三个注解,到底该用哪一个?别再傻傻分不清了!
后端
用户490558160812544 分钟前
当控制面更新一条 ACL 规则时,如何更新给数据面
后端
林太白1 小时前
Nuxt.js搭建一个官网如何简单
前端·javascript·后端
码事漫谈1 小时前
VS Code 终端完全指南
后端
该用户已不存在1 小时前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
怀刃2 小时前
内存监控对应解决方案
后端
码事漫谈2 小时前
VS Code Copilot 内联聊天与提示词技巧指南
后端
Moonbit2 小时前
MoonBit Perals Vol.06: MoonBit 与 LLVM 共舞 (上):编译前端实现
后端·算法·编程语言