Fork me on GitHub

# JDK7+ MethodHandle

简单例子

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MHT {

    private String name;

    // 构造方法
    public MHT(String name) {
        this.name = name;
    }

    // 公共方法
    public String publicTest() {
        return name + "'publicTest";
    }

    // 静态方法
    public static String publicStaticTest() {
        return "'publicStaticTest";
    }

    // 私有方法
    private String test(int param) {
        switch (param) {
            case 1:
                return "suych1";
            case 2:
                return "suych2";
            case 3:
                return "suych3";
            default:
                return "suych4";
        }
    }

    // Get方法
    public String getName() {
        return name;
    }

    // Set方法
    public void setName(String name) {
        this.name = name;
    }

    public static void main(String[] args) throws Throwable {
        // 构造方法
        MethodType mtConstructor = MethodType.methodType(void.class, String.class);  //返回值类型,参数类型
        MethodHandle mhConstructor = MethodHandles.lookup().findConstructor(MHT.class, mtConstructor);
        MHT businessHandle = ( MHT ) mhConstructor.invokeExact("suych");
        System.out.println(businessHandle.getName());

        // 公共方法
        MethodType mtPublic = MethodType.methodType(String.class);  //返回值类型
        MethodHandle mhPublic = MethodHandles.lookup().findVirtual(MHT.class, "publicTest", mtPublic);
        String resultPublic = ( String ) mhPublic.invokeExact(businessHandle);
        System.out.println(resultPublic);

        // 静态方法
        MethodType mtPublicStatic = MethodType.methodType(String.class);
        MethodHandle mhPublicStatic = MethodHandles.lookup().findStatic(MHT.class, "publicStaticTest",
                mtPublicStatic);
        String resultPublicStatic = ( String ) mhPublicStatic.invokeExact();
        System.out.println(resultPublicStatic);

        // 私有方法
        MethodType mtPrivate = MethodType.methodType(String.class, int.class); //返回值类型,参数类型
        MethodHandle mhPrivate = MethodHandles.lookup().findSpecial(MHT.class, "test", mtPrivate,
                MHT.class);
        String resultPrivate = ( String ) mhPrivate.invokeExact(businessHandle, 1);
        System.out.println(resultPrivate);

        // Set方法
        MethodHandle mhSet = MethodHandles.lookup().findSetter(MHT.class, "name", String.class);
        mhSet.invokeExact(businessHandle, "A");
        System.out.println(businessHandle.name);

        // Get方法
        MethodHandle mhGet = MethodHandles.lookup().findGetter(MHT.class, "name", String.class);
        String resultGet = ( String ) mhGet.invokeExact(businessHandle);
        System.out.println(resultGet);

    }

}

输出:

suych   构造方法输出
suych'publicTest   公共方法输出
'publicStaticTest   静态方法输出
suych1	 私有方法输出
A	Set方法输出
A	Get方法输出

基于MethodHandle实现的调用Runtime执行系统命令

import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Scanner;

/**
 * @author yz
 */
public class MethodHandlesTest {

    public static void main(String[] args) {
        try {
            String               str          = "arp -a";
            Class                runtimeClass = Runtime.class;
            MethodHandles.Lookup lookup       = MethodHandles.lookup();

            // Runtime rt = Runtime.getRuntime()
            MethodHandle methodHandle = lookup.findStatic(
                    runtimeClass, "getRuntime", MethodType.methodType(runtimeClass)
            );

            // 获取Runtime的exec方法
            MethodHandle execMethod = lookup.findVirtual(
                    runtimeClass, "exec", MethodType.methodType(Process.class, new Class[]{
                            String.class
                    })
            );

            // 获取Process的getInputStream方法
            MethodHandle inputStreamMethod = lookup.findVirtual(
                    Process.class, "getInputStream", MethodType.methodType(InputStream.class)
            );

            // 调用Runtime.getRuntime().exec(xxx).getInputStream()
            InputStream in = (InputStream) inputStreamMethod.invoke(
                    execMethod.invoke(methodHandle.invoke(), str)
            );

            // 输出InputStream内容到
            Scanner scanner = new Scanner(in).useDelimiter("\\A");
            System.out.println(scanner.hasNext() ? scanner.next() : "");
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

Alt text

JAVA反射修改private,final值

FinalName.java

class FinalName {
    public final String name="init";
}

PrivateName.java

class PrivateName {
    private String name = "init";
    public String getName() {
        return name;
    }
}

Test.java

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        PrivateName privateName = new PrivateName();
        FinalName finalName = new FinalName();
//        System.out.println(finalName.name);
        modify(privateName, "name", "private change");
        modify(finalName, "name", "final change");
        System.out.println(privateName.getName());
        System.out.println(finalName.name);
    }
    public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception {
        Field field = object.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(object, newFieldValue);
        System.out.println(field.get(object));
    }
}

输出结果,private修饰的变量结果已经被修改,但是final修改正常但是获取的依然是原先的值,这是因为内联优化。

private change
final change
private change
init

如果将FinalName.java修改为通过构造方法给final修饰的属性赋值。

class FinalName {
    public final String name;

    FinalName(String name) {
        this.name = name;
    }
}
FinalName Finalname = FinalName("aaaaaa");

再次运行Test.java,发现final修饰的变量已经被修改。

private change
final change
private change
final change

总结:private修饰的变量可以通过反射的方法将值修改,需要设置访问权限为true。field.setAccessible(true);
final修饰的变量如果是直接赋值,则对属性值进行修改无效。如果是通过构造方法修改属性的值,则可以通过反射的方法修改final修饰的变量。

通过反射方式执行命令。

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Scanner;
public class ReflectionTest {

    public static void exec() {
        try {
            System.out.println(Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "curl -i localhost:8000"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            String str = "arp -a";

            // java.lang.Runtime
            String runtime = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101});

            // Runtime.class
            Class<?> c = Class.forName(runtime);

            // 获取getRuntime方法,Runtime.getRuntime()
            Method m1 = c.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101}));

            // 获取Runtime的exec方法,rt.exec(xxx)
            Method m2 = c.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class);

            // Runtime.getRuntime().exec(str)
            Object obj2 = m2.invoke(m1.invoke(null), str);

            // 获取命令执行结果Process类的getInputStream()方法
            Method m = obj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109}));
            m.setAccessible(true);

            // process.getInputStream()
            InputStream in = (InputStream) m.invoke(obj2, new Object[]{});

            // 输出InputStream内容到
            Scanner scanner = new Scanner(in).useDelimiter("\\A");
            System.out.println(scanner.hasNext() ? scanner.next() : "");
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

看下为什么获取命令执行结果Process类的getInputStream()方法访问权限需要设置true。
跟入java.lang.ProcessImpl类
Alt text
getInputStream方法返回stdout_stream是私有变量
Alt text
Alt text
注释掉,则会报下面这样的错误
Alt text

参考链接:
https://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodType.html
https://mp.weixin.qq.com/s/mlqjOlhefcsO9z51cw4S7w
https://blog.csdn.net/yhd723948277/article/details/82661870

posted @ 2019-06-26 15:21  Afant1  阅读(315)  评论(0编辑  收藏  举报