Java 15 提供規格 JEP-371 稱為 Hidden Class 的功能,Hidden Class 提供高效且彈性的功能,可在 run time 動態產生 class。hidden class 無法直接被 byte code 或其他 classes 使用,可以用在特殊的保護的程式碼,讓這些程式碼,不會在 VM 裡面被動態修改。
Hidden Class 的特性
hidden class 動態產生的 class 有以下特性
non-discoverable
無法在 bytecode 被使用,無法被 class loader 使用,無法用 reflective methods ( Class:forName, ClassLoader:findLoadedClass , Lookup:findClass) 找到這些 class
不能將 hidden class 當作 superclass, field tyoe, return type, parameter type
測試
以下測試的概念,是將要被隱藏的類別,先編譯出來,然後把編譯後的 byte code,用 base 64 的方式編碼。
該編碼後的字串,就等同於一個已經編譯過的類別,可以用 Hidden Class 的方式,載入該類別並呼叫該類別的一個靜態 method
hidden/Hello.java
package hidden;
public class Hello {
public static String greet() {
return "hello";
}
}
hidden/ClassToBase64Converter.java
這個 class 是用來將 class byte code 的 byte array,用 base64 編碼
package hidden;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
public class ClassToBase64Converter {
public static void main(String[] args) {
// Provide the path to your .class file
Class<?> clazz = hidden.Hello.class;
String className = clazz.getName();
String classAsPath = className.replace('.', '/') + ".class";
try {
byte[] classBytes;
try (InputStream stream = clazz.getClassLoader().getResourceAsStream(classAsPath)) {
// Read the .class file as bytes
// byte[] classBytes = Files.readAllBytes(Paths.get(classFilePath));
classBytes = stream != null ? stream.readAllBytes() : new byte[0];
}
// Encode the bytes to Base64
String base64Encoded = Base64.getEncoder().encodeToString(classBytes);
// Print or use the Base64-encoded string
System.out.println("Base64 Encoded Class:\n" + base64Encoded);
} catch (Exception e) {
e.printStackTrace();
}
}
}
執行後可得到這個結果
Base64 Encoded Class:
yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAFaGVsbG8HAAoBAAxoaWRkZW4vSGVsbG8BAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEADkxoaWRkZW4vSGVsbG87AQAFZ3JlZXQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAApIZWxsby5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAFAAEAEgAAAAIAEw==
接下來可把這個 base64 字串,放到另一個 class 的 string
package hidden;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Base64;
public class HiddenClassDemo {
static final String CLASS_IN_BASE64 = "yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAFaGVsbG8HAAoBAAxoaWRkZW4vSGVsbG8BAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEADkxoaWRkZW4vSGVsbG87AQAFZ3JlZXQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAApIZWxsby5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAFAAEAEgAAAAIAEw==";
public static void main(String[] args) throws Throwable {
testHiddenClass();
}
// create a hidden class and run its static method
public static void testHiddenClass() throws Throwable {
byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);
Class<?> proxy = MethodHandles.lookup()
.defineHiddenClass(classInBytes,
true, MethodHandles.Lookup.ClassOption.NESTMATE)
.lookupClass();
System.out.println(proxy.getName());
MethodHandle mh = MethodHandles.lookup().findStatic(proxy,
"greet",
MethodType.methodType(String.class));
String status = (String) mh.invokeExact();
System.out.println(status);
}
}
執行結果
hidden.Hello/0x0000000800c00400
hello
References
Hidden Classes in Java 15 | Baeldung
What are Hidden Classes in Java 15 And How Are They Used? | foojay
沒有留言:
張貼留言