反射是唯一方法调用的途径嘛?不妨看看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-使用方法及性能测试

相关推荐
哎呦没11 分钟前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
2401_8576009514 分钟前
SpringBoot框架的企业资产管理自动化
spring boot·后端·自动化
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk6 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*6 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue6 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man6 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源