怎么做網(wǎng)上賭博的網(wǎng)站寧波網(wǎng)絡(luò)推廣平臺(tái)
一、lambda 代碼 & 反編譯
原始Java代碼
假設(shè)我們有以下簡(jiǎn)單的Java程序,它使用Lambda表達(dá)式來遍歷并打印一個(gè)字符串列表:
import java.util.Arrays;
import java.util.List;public class LambdaExample {public static void main(String[] args) {List<String> items = Arrays.asList("Apple", "Banana", "Cherry");items.forEach(item -> System.out.println(item));}
}
public interface Iterable<T> {default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}}
}
CFR反編譯結(jié)果:
/** Decompiled with CFR 0.152.*/
import java.lang.invoke.LambdaMetafactory;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;public class LambdaExample {public static void main(String[] stringArray) {List<String> list = Arrays.asList("Apple", "Banana", "Cherry");list.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());}private static /* synthetic */ void lambda$main$0(String string) {System.out.println(string);}
}
這是程序的主方法,它創(chuàng)建了一個(gè)包含三個(gè)字符串的列表,并使用forEach方法遍歷這個(gè)列表。在原始的Java代碼中,這里很可能使用了一個(gè)Lambda表達(dá)式來打印列表中的每個(gè)元素。在反編譯的代碼中,Lambda表達(dá)式被轉(zhuǎn)換成了對(duì)LambdaMetafactory.metafactory方法的調(diào)用,這個(gè)方法在運(yùn)行時(shí)動(dòng)態(tài)生成了一個(gè)實(shí)現(xiàn)了Consumer接口的類的實(shí)例。因?yàn)閒orEach方法入?yún)⒕褪且粋€(gè)函數(shù)式接口Consumer<? super T>,即:最終返回Consumer實(shí)例對(duì)象
二、反編譯代碼詳解
2.1 LambdaMetafactory lambda元工廠類 方法:metafactory
/*** 為了支持Java編程語言中的ambda表達(dá)式和方法引用表達(dá)式特性,* 本方法提供了一種簡(jiǎn)便的方式來創(chuàng)建實(shí)現(xiàn)一個(gè)或多個(gè)接口的“函數(shù)對(duì)象”。這些函數(shù)對(duì)象是通過委托給一個(gè)提供的{@link MethodHandle},* 在適當(dāng)?shù)念愋瓦m配和參數(shù)的部分求值之后實(shí)現(xiàn)的。通常作為{@code invokedynamic}調(diào)用點(diǎn)的<em>引導(dǎo)方法</em>使用。** <p>這是標(biāo)準(zhǔn)的、簡(jiǎn)化的元工廠方法;通過{@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}* 提供了額外的靈活性。關(guān)于此方法的行為的一般描述,請(qǐng)參見{@link LambdaMetafactory}。** <p>當(dāng)從此方法返回的{@code CallSite}的目標(biāo)被調(diào)用時(shí),生成的函數(shù)對(duì)象是實(shí)現(xiàn)由{@code invokedType}的返回類型命名的接口的類的實(shí)例,* 聲明了一個(gè)具有由{@code invokedName}和{@code samMethodType}給出的名稱和簽名的方法。它還可能覆蓋來自{@code Object}的額外方法。** @param caller 表示具有調(diào)用者訪問權(quán)限的查找上下文。當(dāng)與{@code invokedynamic}一起使用時(shí),這由VM自動(dòng)堆疊。* @param invokedName 要實(shí)現(xiàn)的方法的名稱。當(dāng)與{@code invokedynamic}一起使用時(shí),這由{@code InvokeDynamic}結(jié)構(gòu)的{@code NameAndType}提供,并由VM自動(dòng)堆疊。* @param invokedType {@code CallSite}的預(yù)期簽名。參數(shù)類型代表捕獲變量的類型;返回類型是要實(shí)現(xiàn)的接口。當(dāng)與{@code invokedynamic}一起使用時(shí),這由{@code InvokeDynamic}結(jié)構(gòu)的{@code NameAndType}提供,并由VM自動(dòng)堆疊。如果實(shí)現(xiàn)方法是實(shí)例方法并且此簽名有任何參數(shù),則調(diào)用簽名中的第一個(gè)參數(shù)必須對(duì)應(yīng)于接收者。* @param samMethodType 函數(shù)對(duì)象要實(shí)現(xiàn)的方法的簽名和返回類型。* @param implMethod 描述應(yīng)在調(diào)用時(shí)調(diào)用的實(shí)現(xiàn)方法的直接方法句柄(適當(dāng)?shù)剡m配參數(shù)類型、返回類型,并將捕獲的參數(shù)前置到調(diào)用參數(shù)中)。* @param instantiatedMethodType 應(yīng)在調(diào)用時(shí)動(dòng)態(tài)強(qiáng)制執(zhí)行的簽名和返回類型。這可能與{@code samMethodType}相同,或可能是其特化版本。* @return 一個(gè)CallSite,其目標(biāo)可用于執(zhí)行捕獲,生成由{@code invokedType}命名的接口的實(shí)例* @throws LambdaConversionException 如果違反了{(lán)@link LambdaMetafactory}中描述的任何鏈接不變量*/public static CallSite metafactory(MethodHandles.Lookup caller,String invokedName,MethodType invokedType,MethodType samMethodType,MethodHandle implMethod,MethodType instantiatedMethodType)throws LambdaConversionException {// 創(chuàng)建一個(gè)內(nèi)部類Lambda元工廠實(shí)例,用于生成和驗(yàn)證lambda表達(dá)式的實(shí)現(xiàn)AbstractValidatingLambdaMetafactory mf;mf = new InnerClassLambdaMetafactory(caller, invokedType,invokedName, samMethodType,implMethod, instantiatedMethodType,false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);// 驗(yàn)證元工廠方法的參數(shù)是否符合要求mf.validateMetafactoryArgs();// 構(gòu)建并返回一個(gè)CallSite,它是lambda表達(dá)式或方法引用的動(dòng)態(tài)調(diào)用點(diǎn)return mf.buildCallSite();}
java.lang.invoke.LambdaMetafactory#metafactory 詳解
metafactory是LambdaMetafactory中的一個(gè)靜態(tài)方法,用于支持lambda表達(dá)式和方法引用表達(dá)式的動(dòng)態(tài)實(shí)現(xiàn)。它是LambdaMetafactory
類的一部分,該類是Java語言中l(wèi)ambda表達(dá)式和方法引用的底層支持機(jī)制。下面是對(duì)這段代碼的詳細(xì)解釋:
-
方法的作用和目的:
這個(gè)方法的目的是為了動(dòng)態(tài)創(chuàng)建一個(gè)實(shí)現(xiàn)特定接口的"函數(shù)對(duì)象"。這個(gè)函數(shù)對(duì)象通過委托給一個(gè)提供的MethodHandle
(方法句柄),在適當(dāng)?shù)念愋瓦m配和參數(shù)的部分求值后,實(shí)現(xiàn)一個(gè)或多個(gè)接口。這通常用作invokedynamic
調(diào)用點(diǎn)的引導(dǎo)方法(bootstrap method),以支持Java編程語言中的lambda表達(dá)式和方法引用表達(dá)式特性。 -
參數(shù):
- 方法接收六個(gè)參數(shù):
caller
、invokedName
、invokedType
、samMethodType
、implMethod
和instantiatedMethodType
。caller
:調(diào)用者,這個(gè)例子中就是LambdaExample類的MethodHandles.Lookup實(shí)例(每個(gè)類都可以通過調(diào)用MethodHandles.lookup()靜態(tài)方法來獲取一個(gè)與該類對(duì)應(yīng)的MethodHandles.Lookup實(shí)例。這個(gè)Lookup實(shí)例代表了調(diào)用者的類,并且擁有創(chuàng)建方法句柄(MethodHandle)的權(quán)限,這些方法句柄可以訪問調(diào)用者類中的成員,包括私有成員。),這個(gè)實(shí)例具有訪問LambdaExample類中所有成員的權(quán)限。該參數(shù)是jvm自動(dòng)填充。invokedName
:被調(diào)用方法的名稱,在這個(gè)例子中,forEach方法接受一個(gè)java.util.function.Consumer類型的參數(shù)。Consumer接口定義了一個(gè)名為accept的抽象方法。因此,在這個(gè)上下文中,invokedName將是accept。 該參數(shù)是jvm自動(dòng)填充。invokedType
:被調(diào)用方法的簽名類型,這是一個(gè)java.lang.invoke.MethodType對(duì)象。在lambda表達(dá)式或方法引用的上下文中,invokedType描述了期望的調(diào)用點(diǎn)的簽名,包括參數(shù)類型和返回類型。具體來說,invokedType參數(shù)定義了:
①調(diào)用點(diǎn)期望的參數(shù)類型,這些參數(shù)類型代表了lambda表達(dá)式或方法引用捕獲的變量類型(如果有的話)。
②調(diào)用點(diǎn)期望的返回類型,這通常是一個(gè)函數(shù)式接口的類型,lambda表達(dá)式或方法引用將會(huì)生成一個(gè)實(shí)現(xiàn)了這個(gè)接口的對(duì)象。 在這個(gè)例子中,forEach方法接受一個(gè)java.util.function.Consumer類型的參數(shù)。Consumer接口定義了一個(gè)接受單個(gè)String參數(shù)且返回void的accept方法。因此,在這個(gè)上下文中,invokedType將是Consumer的方法簽名,即接受一個(gè)String參數(shù)且返回void的方法類型。 該參數(shù)是jvm自動(dòng)填充。samMethodType
:java.lang.invoke.LambdaMetafactory#metafactory方法的參數(shù)samMethodType指的是單抽象方法(Single Abstract Method, SAM)的方法類型。這是一個(gè)java.lang.invoke.MethodType對(duì)象,它描述了目標(biāo)函數(shù)式接口中單個(gè)抽象方法的簽名,包括參數(shù)類型和返回類型。
在使用lambda表達(dá)式或方法引用時(shí),通常會(huì)有一個(gè)函數(shù)式接口作為目標(biāo)類型。函數(shù)式接口是指僅定義一個(gè)抽象方法的接口。samMethodType參數(shù)正是用來描述這個(gè)抽象方法的簽名。在這個(gè)例子中,forEach方法接受一個(gè)java.util.function.Consumer類型的參數(shù)。Consumer是一個(gè)函數(shù)式接口,它定義了一個(gè)名為accept的抽象方法,該方法接受一個(gè)類型為T的參數(shù)并返回void。對(duì)于這個(gè)特定的例子,T是String類型,因此accept方法的簽名是(String) -> void。implMethod
:java.lang.invoke.LambdaMetafactory#metafactory方法的參數(shù)implMethod指的是實(shí)現(xiàn)方法的MethodHandle。這個(gè)MethodHandle代表了lambda表達(dá)式或方法引用的實(shí)際實(shí)現(xiàn)體。在lambda表達(dá)式或方法引用被轉(zhuǎn)換成動(dòng)態(tài)方法調(diào)用時(shí),implMethod就是那個(gè)被調(diào)用以執(zhí)行具體操作的方法。
具體來說,implMethod參數(shù)描述了:
①方法的實(shí)現(xiàn):這是lambda表達(dá)式或方法引用中定義的邏輯的實(shí)際代碼位置。
②方法的簽名:通過MethodHandle的類型,它還隱含地指定了方法的參數(shù)類型和返回類型。
在這個(gè)例子中,lambda表達(dá)式item -> System.out.println(item)對(duì)應(yīng)的implMethod就是System.out.println(String)方法的MethodHandle。這個(gè)MethodHandle指向PrintStream類中的println(String)方法,這是因?yàn)镾ystem.out是一個(gè)PrintStream的實(shí)例。instantiatedMethodType
:指的是實(shí)例化方法的類型。這是一個(gè)java.lang.invoke.MethodType對(duì)象,它描述了在生成的lambda表達(dá)式或方法引用的實(shí)例中,目標(biāo)方法的簽名。具體來說,它定義了lambda表達(dá)式或方法引用在實(shí)現(xiàn)函數(shù)式接口時(shí),該接口中抽象方法的調(diào)用簽名,包括參數(shù)類型和返回類型。在這個(gè)例子中,forEach方法接受一個(gè)java.util.function.Consumer類型的參數(shù)。Consumer是一個(gè)函數(shù)式接口,它定義了一個(gè)名為accept的抽象方法,該方法接受一個(gè)類型為String的參數(shù)并返回void。因此,對(duì)于這個(gè)特定的例子,instantiatedMethodType將是描述accept方法簽名的MethodType對(duì)象,即接受一個(gè)String參數(shù)且返回void的方法類型。
- 方法接收六個(gè)參數(shù):
-
邏輯解釋:
AbstractValidatingLambdaMetafactory mf;
:聲明一個(gè)AbstractValidatingLambdaMetafactory
類型的變量mf
,這是一個(gè)抽象類,用于驗(yàn)證lambda工廠的參數(shù)。mf = new InnerClassLambdaMetafactory(caller, invokedType, invokedName, samMethodType, implMethod, instantiatedMethodType, false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
:實(shí)例化mf
為InnerClassLambdaMetafactory
對(duì)象,這個(gè)對(duì)象負(fù)責(zé)創(chuàng)建實(shí)現(xiàn)特定接口的函數(shù)對(duì)象。傳入的參數(shù)包括調(diào)用者的查找上下文、被調(diào)用方法的名稱和類型、SAM(Single Abstract Method)接口的方法類型、實(shí)現(xiàn)方法的方法句柄、以及實(shí)例化方法的類型。false
表示這個(gè)lambda對(duì)象不需要是可序列化的,EMPTY_CLASS_ARRAY
和EMPTY_MT_ARRAY
分別表示沒有額外的接口和方法類型需要被實(shí)現(xiàn)或適配。mf.validateMetafactoryArgs();
:調(diào)用mf
的validateMetafactoryArgs
方法進(jìn)行參數(shù)驗(yàn)證,確保傳入的參數(shù)滿足lambda表達(dá)式和方法引用的鏈接要求。return mf.buildCallSite();
:調(diào)用mf
的buildCallSite
方法構(gòu)建并返回一個(gè)CallSite
對(duì)象,這個(gè)對(duì)象的目標(biāo)可以用來執(zhí)行捕獲,生成實(shí)現(xiàn)了指定接口的實(shí)例。
CallSite
是Java中的一個(gè)類,它代表了一個(gè)動(dòng)態(tài)方法調(diào)用點(diǎn)。在Java7的動(dòng)態(tài)語言支持中,CallSite
提供了一種機(jī)制,允許方法調(diào)用的行為在運(yùn)行時(shí)動(dòng)態(tài)改變,而不是在編譯時(shí)靜態(tài)確定。這對(duì)于實(shí)現(xiàn)動(dòng)態(tài)類型語言或支持某些高級(jí)動(dòng)態(tài)特性的靜態(tài)類型語言(如Java中的lambda表達(dá)式和方法引用)非常有用。CallSite
對(duì)象包含一個(gè)稱為目標(biāo)(target)的MethodHandle
,這個(gè)MethodHandle
實(shí)際上定義了調(diào)用點(diǎn)的行為。當(dāng)對(duì)CallSite
進(jìn)行方法調(diào)用時(shí),實(shí)際上是在調(diào)用其目標(biāo)MethodHandle
。
Java中的CallSite
有幾種不同的類型,包括:MethodHandleNatives.CallSite
:這是最基本的CallSite
,直接關(guān)聯(lián)一個(gè)MethodHandle
作為其調(diào)用目標(biāo)。ConstantCallSite
:一個(gè)不可變的CallSite
,其目標(biāo)在構(gòu)造時(shí)被設(shè)置,并且之后不能改變。這對(duì)于那些不需要改變的方法調(diào)用非常有用,可以提供更好的性能。MutableCallSite
:一個(gè)可變的CallSite
,允許改變其目標(biāo)MethodHandle
。這對(duì)于需要根據(jù)運(yùn)行時(shí)條件改變調(diào)用行為的情況非常有用。VolatileCallSite
:類似于MutableCallSite
,但是對(duì)目標(biāo)MethodHandle
的更新是volatile的,確保了線程安全。
CallSite
和MethodHandle
是Java對(duì)動(dòng)態(tài)語言特性的支持的核心部分,它們使得Java能夠以更靈活和動(dòng)態(tài)的方式處理方法調(diào)用,支持如lambda表達(dá)式和方法引用等現(xiàn)代編程特性。metafactory
方法返回的調(diào)用點(diǎn)是CallSite
的一個(gè)實(shí)例。具體來說,根據(jù)LambdaMetafactory
的實(shí)現(xiàn),它通常返回的是ConstantCallSite
的一個(gè)實(shí)例。ConstantCallSite
是CallSite
的一個(gè)子類,它表示一個(gè)不可變的調(diào)用點(diǎn)。一旦ConstantCallSite
的目標(biāo)方法句柄(MethodHandle)被設(shè)置,它就不會(huì)改變。這種特性使得ConstantCallSite
非常適合于lambda表達(dá)式和方法引用的場(chǎng)景,因?yàn)檫@些場(chǎng)景中的目標(biāo)方法通常在創(chuàng)建時(shí)就已經(jīng)確定,并且在其生命周期內(nèi)不需要改變。
·
在LambdaMetafactory
的上下文中,metafactory
方法通過動(dòng)態(tài)生成的類來實(shí)現(xiàn)函數(shù)接口,并創(chuàng)建一個(gè)指向這個(gè)實(shí)現(xiàn)的方法句柄(MethodHandle)。然后,這個(gè)方法句柄被用作ConstantCallSite
的目標(biāo),從而創(chuàng)建一個(gè)CallSite
實(shí)例。這個(gè)CallSite
實(shí)例在被調(diào)用時(shí),會(huì)直接調(diào)用那個(gè)實(shí)現(xiàn)了函數(shù)接口的動(dòng)態(tài)生成類的方法。
這段代碼通過動(dòng)態(tài)創(chuàng)建和配置CallSite
對(duì)象,支持了Java中l(wèi)ambda表達(dá)式和方法引用表達(dá)式的動(dòng)態(tài)實(shí)現(xiàn)。
2.2、InnerClassLambdaMetafactory
InnerClassLambdaMetafactory構(gòu)造函數(shù)、buildCallSite構(gòu)建CallSite調(diào)用點(diǎn)
/*** 構(gòu)造函數(shù):創(chuàng)建一個(gè)內(nèi)部類Lambda元工廠的實(shí)例。* 該構(gòu)造函數(shù)用于支持標(biāo)準(zhǔn)情況以及允許序列化或橋接等不常見選項(xiàng)。** @param caller 由VM自動(dòng)堆疊;代表具有調(diào)用者訪問權(quán)限的查找上下文。* @param invokedType 由VM自動(dòng)堆疊;被調(diào)用方法的簽名,包括返回的lambda對(duì)象的預(yù)期靜態(tài)類型,* 以及l(fā)ambda捕獲參數(shù)的靜態(tài)類型。如果實(shí)現(xiàn)方法是實(shí)例方法,調(diào)用簽名的第一個(gè)參數(shù)將對(duì)應(yīng)于接收者。* @param samMethodName 轉(zhuǎn)換為lambda或方法引用的函數(shù)接口中的方法名稱,表示為String。* @param samMethodType 轉(zhuǎn)換為lambda或方法引用的函數(shù)接口中的方法類型,表示為MethodType。* @param implMethod 應(yīng)當(dāng)被調(diào)用的實(shí)現(xiàn)方法(適當(dāng)調(diào)整參數(shù)類型、返回類型和捕獲參數(shù)后),當(dāng)調(diào)用結(jié)果函數(shù)接口實(shí)例的方法時(shí)。* @param instantiatedMethodType 在從捕獲站點(diǎn)實(shí)例化類型變量后,主要函數(shù)接口方法的簽名。* @param isSerializable lambda是否應(yīng)該是可序列化的?如果設(shè)置,目標(biāo)類型或一個(gè)附加的SAM類型必須擴(kuò)展{@code Serializable}。* @param markerInterfaces lambda對(duì)象應(yīng)該實(shí)現(xiàn)的附加接口。* @param additionalBridges 額外的簽名,這些簽名將被橋接到實(shí)現(xiàn)方法。* @throws LambdaConversionException 如果違反了元工廠協(xié)議的任何不變量。*/public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,MethodType invokedType,String samMethodName,MethodType samMethodType,MethodHandle implMethod,MethodType instantiatedMethodType,boolean isSerializable,Class<?>[] markerInterfaces,MethodType[] additionalBridges)throws LambdaConversionException {// 調(diào)用父類構(gòu)造函數(shù),初始化基本參數(shù)super(caller, invokedType, samMethodName, samMethodType,implMethod, instantiatedMethodType,isSerializable, markerInterfaces, additionalBridges);// 初始化實(shí)現(xiàn)方法的類名,將'.'替換為'/'implMethodClassName = implDefiningClass.getName().replace('.', '/');// 初始化實(shí)現(xiàn)方法的名稱implMethodName = implInfo.getName();// 初始化實(shí)現(xiàn)方法的描述符implMethodDesc = implMethodType.toMethodDescriptorString();// 初始化實(shí)現(xiàn)方法返回類型的類implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)? implDefiningClass: implMethodType.returnType();// 初始化生成類構(gòu)造函數(shù)的類型constructorType = invokedType.changeReturnType(Void.TYPE);// 生成并初始化lambda類的名稱lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();// 【重要??????????】初始化ASM類寫入器cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);// 初始化構(gòu)造函數(shù)參數(shù)名稱和描述符數(shù)組int parameterCount = invokedType.parameterCount();if (parameterCount > 0) {// 初始化參數(shù)名和參數(shù)描述數(shù)組,大小為方法參數(shù)的數(shù)量argNames = new String[parameterCount];argDescs = new String[parameterCount];// 遍歷所有參數(shù),生成參數(shù)名和參數(shù)描述for (int i = 0; i < parameterCount; i++) {// 為每個(gè)參數(shù)生成一個(gè)唯一的名稱,格式為"arg$序號(hào)"argNames[i] = "arg$" + (i + 1);// 使用BytecodeDescriptor工具類將參數(shù)類型轉(zhuǎn)換為字符串描述形式argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));}} else {// 當(dāng)調(diào)用類型參數(shù)計(jì)數(shù)為0時(shí),初始化參數(shù)名和參數(shù)描述數(shù)組為空字符串?dāng)?shù)組argNames = argDescs = EMPTY_STRING_ARRAY;}}/*** 構(gòu)建CallSite。生成實(shí)現(xiàn)功能接口的類文件,定義類,如果沒有參數(shù)則創(chuàng)建類的實(shí)例,* 該實(shí)例將由CallSite返回,否則,生成的句柄將調(diào)用類的構(gòu)造函數(shù)。** @return CallSite,調(diào)用時(shí),將返回一個(gè)功能接口的實(shí)例* @throws ReflectiveOperationException 反射操作異常* @throws LambdaConversionException 如果沒有找到正確形式的功能接口*/@OverrideCallSite buildCallSite() throws LambdaConversionException {// 生成實(shí)現(xiàn)了函數(shù)接口的內(nèi)部類final Class<?> innerClass = spinInnerClass();// 如果調(diào)用類型沒有參數(shù),即無需捕獲的變量if (invokedType.parameterCount() == 0) {// 通過反射獲取一個(gè)內(nèi)部類的所有構(gòu)造函數(shù),并在只有一個(gè)構(gòu)造函數(shù)的情況下,將這個(gè)唯一的構(gòu)造函數(shù)設(shè)置為可訪問的。final Constructor<?>[] ctrs = AccessController.doPrivileged(new PrivilegedAction<Constructor<?>[]>() {@Overridepublic Constructor<?>[] run() {// 返回了innerClass(內(nèi)部類)的所有構(gòu)造函數(shù),包括私有的。Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();if (ctrs.length == 1) {// 如果只有一個(gè)構(gòu)造函數(shù),設(shè)置為可訪問ctrs[0].setAccessible(true);}return ctrs;}});// 確保只有一個(gè)構(gòu)造函數(shù)if (ctrs.length != 1) {throw new LambdaConversionException("Expected one lambda constructor for "+ innerClass.getCanonicalName() + ", got " + ctrs.length);}try {// 通過構(gòu)造函數(shù)實(shí)例化對(duì)象Object inst = ctrs[0].newInstance();// 創(chuàng)建并返回一個(gè)持有l(wèi)ambda對(duì)象的ConstantCallSitereturn new ConstantCallSite(MethodHandles.constant(samBase, inst));}catch (ReflectiveOperationException e) {throw new LambdaConversionException("Exception instantiating lambda object", e);}} else {// 如果有參數(shù),需要通過靜態(tài)方法來創(chuàng)建CallSitetry {// 確保類已經(jīng)被完全初始化UNSAFE.ensureClassInitialized(innerClass);// 查找靜態(tài)方法并創(chuàng)建CallSitereturn new ConstantCallSite(MethodHandles.Lookup.IMPL_LOOKUP.findStatic(innerClass, NAME_FACTORY, invokedType));}catch (ReflectiveOperationException e) {throw new LambdaConversionException("Exception finding constructor", e);}}}/*** 生成并返回一個(gè)實(shí)現(xiàn)了功能接口的類文件。** @implNote 生成的類文件不包含SAM方法可能存在的異常簽名信息,* 旨在減少類文件大小。這是無害的,因?yàn)橐褭z查的異常會(huì)被擦除,* 沒有人會(huì)針對(duì)這個(gè)類文件進(jìn)行編譯,我們不保證lambda對(duì)象的反射屬性。** @return 實(shí)現(xiàn)了功能接口的類* @throws LambdaConversionException 如果沒有找到正確形式的功能接口*/private Class<?> spinInnerClass() throws LambdaConversionException {// 構(gòu)建一個(gè)字符串?dāng)?shù)組 interfaces,該數(shù)組包含了要實(shí)現(xiàn)的接口的內(nèi)部名稱(即將.替換為/的全限定類名),同時(shí)確保沒有重復(fù)的接口,并檢查是否意外地實(shí)現(xiàn)了 Serializable 接口。String[] interfaces;// 獲取函數(shù)式接口的內(nèi)部名稱,將.替換為/。String samIntf = samBase.getName().replace('.', '/');// 檢查基礎(chǔ)函數(shù)式接口是否意外實(shí)現(xiàn)了 Serializable 接口。boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);// 如果沒有額外的標(biāo)記接口,直接使用函數(shù)式接口的內(nèi)部名稱作為 interfaces 的唯一元素。if (markerInterfaces.length == 0) {interfaces = new String[]{samIntf};} else {// 如果 markerInterfaces 非空,確保沒有重復(fù)的接口(ClassFormatError),使用 LinkedHashSet 來存儲(chǔ)接口名稱,確保不會(huì)有重復(fù)。Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);// 將函數(shù)式接口的內(nèi)部名稱添加到集合中itfs.add(samIntf);// 遍歷額外的標(biāo)記接口,將它們的內(nèi)部名稱添加到集合中,并檢查是否意外實(shí)現(xiàn)了 Serializable 接口。for (Class<?> markerInterface : markerInterfaces) {itfs.add(markerInterface.getName().replace('.', '/'));accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);}// 將接口名稱集合轉(zhuǎn)換為字符串?dāng)?shù)組。interfaces = itfs.toArray(new String[itfs.size()]);}/**cw 是 ClassWriter 的實(shí)例,它是 ASM(一個(gè)通用的 Java 字節(jié)碼操作和分析框架)庫(kù)中的一個(gè)類。ClassWriter 用于動(dòng)態(tài)生成類或接口的二進(jìn)制字節(jié)碼。在上下文中,cw 被用來構(gòu)建和定義一個(gè)新的類,這個(gè)類是在運(yùn)行時(shí)動(dòng)態(tài)生成的,用于實(shí)現(xiàn)特定的功能接口,通常是為了支持 Java 中的 lambda 表達(dá)式。
通過調(diào)用 ClassWriter 的方法,如 visit、visitMethod 和 visitField,可以分別定義類的基本信息、方法和字段。最終,通過調(diào)用 cw.toByteArray() 方法,可以獲取到這個(gè)動(dòng)態(tài)生成的類的字節(jié)碼數(shù)組,這個(gè)數(shù)組可以被加載到 JVM 中,從而創(chuàng)建出一個(gè)新的類實(shí)例。*/// 定義了一個(gè)類,這個(gè)類是final和synthetic的,繼承自O(shè)bject類,并實(shí)現(xiàn)了interfaces數(shù)組中指定的接口。lambdaClassName是這個(gè)類的名稱。// 其中:// ACC_FINAL 表示這個(gè)類是final的// ACC_SYNTHETIC 表示這個(gè)類是synthetic的,synthetic標(biāo)記表明這個(gè)類是由編譯器自動(dòng)生成的,而非直接來自源代碼。// lambdaClassName 是動(dòng)態(tài)生成的類名cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,lambdaClassName, null,JAVA_LANG_OBJECT, interfaces);// 生成構(gòu)造函數(shù)中要填充的最終字段for (int i = 0; i < argDescs.length; i++) {// 生成一個(gè)private final字段來存儲(chǔ)這些參數(shù)的值。FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,argNames[i],argDescs[i],null, null);/**這行代碼的作用是結(jié)束一個(gè)字段的訪問。在ASM中,每當(dāng)開始定義一個(gè)新的字段時(shí),都會(huì)通過調(diào)用visitField方法返回一個(gè)FieldVisitor對(duì)象,通過這個(gè)對(duì)象可以定義字段的屬性。當(dāng)字段的定義結(jié)束時(shí),需要調(diào)用visitEnd方法來標(biāo)志這個(gè)過程的結(jié)束。*/ fv.visitEnd();}// 生成構(gòu)造函數(shù)generateConstructor();// 判斷是檢查invokedType(lambda表達(dá)式的目標(biāo)類型)是否有參數(shù)。if (invokedType.parameterCount() != 0) {// 這個(gè)方法的作用是生成工廠方法。工廠方法是一個(gè)特殊的方法,用于動(dòng)態(tài)生成并返回實(shí)現(xiàn)了函數(shù)式接口的類的實(shí)例。這個(gè)過程通常涉及到字節(jié)碼的生成和類的加載。generateFactory();}/**這行代碼通過調(diào)用 ClassWriter 的 visitMethod 方法創(chuàng)建了一個(gè)新的方法。這個(gè)方法的訪問級(jí)別是 public,方法名是 samMethodName,這是一個(gè)從外部傳入的參數(shù),表示要實(shí)現(xiàn)的SAM接口中的方法名。samMethodType.toMethodDescriptorString() 將方法的簽名轉(zhuǎn)換為字符串形式,用于定義方法的參數(shù)類型和返回類型。最后兩個(gè) null 參數(shù)分別表示這個(gè)方法的簽名和異常,這里不使用這些高級(jí)特性,所以傳入 null。*/MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,samMethodType.toMethodDescriptorString(), null, null);// 這行代碼給剛才創(chuàng)建的方法添加了一個(gè)注解 LambdaForm$Hidden。這個(gè)注解是內(nèi)部使用的,用于標(biāo)記這個(gè)方法不應(yīng)該被外部調(diào)用或者看到。true 參數(shù)表示這個(gè)注解是在運(yùn)行時(shí)可見的。mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);// 這行代碼實(shí)際上是生成方法體的關(guān)鍵步驟。它創(chuàng)建了一個(gè) ForwardingMethodGenerator 對(duì)象,這個(gè)對(duì)象負(fù)責(zé)生成方法體的字節(jié)碼。generate 方法接受一個(gè) MethodType 對(duì)象 samMethodType 作為參數(shù),這個(gè)對(duì)象描述了SAM接口方法的參數(shù)類型和返回類型。generate 方法根據(jù)這個(gè)信息,動(dòng)態(tài)生成字節(jié)碼,這些字節(jié)碼實(shí)現(xiàn)了將調(diào)用轉(zhuǎn)發(fā)到實(shí)際的目標(biāo)方法上。new ForwardingMethodGenerator(mv).generate(samMethodType);/**這段代碼的主要作用是為了生成橋接方法(Bridge Methods),這些方法用于處理泛型擦除后的類型不匹配問題。在Java中,泛型信息在編譯時(shí)會(huì)被擦除,而橋接方法則用于在運(yùn)行時(shí)保持類型的正確性。這段代碼是在動(dòng)態(tài)生成的類中添加這些橋接方法的過程。*/// additionalBridges 是一個(gè)包含了需要生成橋接方法的 MethodType 對(duì)象的數(shù)組。if (additionalBridges != null) {for (MethodType mt : additionalBridges) {// 為每個(gè)橋接方法類型生成方法:通過調(diào)用 cw.visitMethod 方法生成橋接方法。這里的 cw 是一個(gè) ClassWriter 對(duì)象,用于動(dòng)態(tài)生成類的字節(jié)碼。ACC_PUBLIC|ACC_BRIDGE 是方法的訪問標(biāo)志,表示這是一個(gè)公開的橋接方法。samMethodName 是要實(shí)現(xiàn)的函數(shù)式接口的方法名,mt.toMethodDescriptorString() 將方法類型轉(zhuǎn)換為方法描述符字符串,用于指定方法的簽名。mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,mt.toMethodDescriptorString(), null, null);// 添加方法注解:通過調(diào)用 mv.visitAnnotation 方法為生成的橋接方法添加注解。這里的注解是 "Ljava/lang/invoke/LambdaForm$Hidden;",表示這個(gè)方法是由lambda表達(dá)式生成的,不應(yīng)該被直接調(diào)用。mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);// 生成方法體:通過創(chuàng)建一個(gè)新的 ForwardingMethodGenerator 對(duì)象并調(diào)用其 generate 方法來生成橋接方法的方法體。這個(gè)方法體基本上是將調(diào)用轉(zhuǎn)發(fā)到實(shí)際的實(shí)現(xiàn)方法上。new ForwardingMethodGenerator(mv).generate(mt);}}/**這段代碼的作用是根據(jù)是否需要序列化,生成對(duì)應(yīng)的方法。具體來說,如果需要生成的類是可序列化的,則生成序列化友好的方法;如果不是故意的可序列化(即無意中成為可序列化的),則生成序列化敵對(duì)的方法。最后,調(diào)用 cw.visitEnd() 來完成類的定義。1.判斷是否需要序列化:通過 if (isSerializable) 判斷,如果 isSerializable 為 true,則表示需要生成的類是可序列化的,此時(shí)會(huì)調(diào)用 generateSerializationFriendlyMethods() 方法生成序列化友好的方法。2.判斷是否無意中成為可序列化:如果 isSerializable 為 false,則進(jìn)入 else if (accidentallySerializable) 判斷,accidentallySerializable 為 true 表示類無意中成為了可序列化的(例如,通過實(shí)現(xiàn)了某個(gè)可序列化的接口)。此時(shí)會(huì)調(diào)用 generateSerializationHostileMethods() 方法生成序列化敵對(duì)的方法,這可能是為了避免序列化帶來的潛在問題或性能影響。3.完成類的定義:無論是否需要序列化,最后都會(huì)執(zhí)行 cw.visitEnd(),這是ASM庫(kù)中的方法,用于完成類的定義。這一步是生成類文件的最后一步,標(biāo)志著類定義的結(jié)束。*/if (isSerializable)generateSerializationFriendlyMethods();else if (accidentallySerializable)generateSerializationHostileMethods();cw.visitEnd();// 這行代碼調(diào)用 ClassWriter 對(duì)象的 toByteArray 方法,將動(dòng)態(tài)生成的類轉(zhuǎn)換為字節(jié)碼數(shù)組。cw 是 ClassWriter 的實(shí)例,它負(fù)責(zé)生成類的字節(jié)碼。final byte[] classBytes = cw.toByteArray();/**這段代碼首先檢查 dumper 對(duì)象是否為 null。dumper 是一個(gè)可能用于將字節(jié)碼寫入文件的工具對(duì)象。如果 dumper 不為 null,則執(zhí)行以下步驟:1.使用 AccessController.doPrivileged 方法執(zhí)行一個(gè)特權(quán)操作。這是因?yàn)閷懭胛募赡苄枰囟ǖ臋?quán)限,特別是在啟用了安全管理器的環(huán)境中。2.在 doPrivileged 方法中,執(zhí)行一個(gè) PrivilegedAction,其 run 方法調(diào)用 dumper.dumpClass 方法,將類名 lambdaClassName 和字節(jié)碼數(shù)組 classBytes 傳遞給它,以便將字節(jié)碼寫入文件。3.doPrivileged 方法的第二個(gè)參數(shù)是 null,表示不使用特定的 AccessControlContext。4.第三和第四個(gè)參數(shù)是 FilePermission 和 PropertyPermission 對(duì)象,分別授予讀寫所有文件的權(quán)限和讀取用戶當(dāng)前目錄的權(quán)限。這些權(quán)限是執(zhí)行文件寫入操作所必需的。*/// 轉(zhuǎn)儲(chǔ)到文件if (dumper != null) {AccessController.doPrivileged(new PrivilegedAction<Void>() {@Overridepublic Void run() {dumper.dumpClass(lambdaClassName, classBytes);return null;}}, null,new FilePermission("<<ALL FILES>>", "read, write"),// 創(chuàng)建目錄可能需要它new PropertyPermission("user.dir", "read"));}/**1.代碼的作用下面這段代碼的作用是在運(yùn)行時(shí)動(dòng)態(tài)定義一個(gè)匿名類。UNSAFE.defineAnonymousClass 方法接收三個(gè)參數(shù):目標(biāo)類(targetClass),類的字節(jié)碼(classBytes),以及與類相關(guān)聯(lián)的常量池補(bǔ)丁(這里傳入的是 null)。2.代碼的結(jié)構(gòu)和邏輯2.1 targetClass:這是一個(gè) Class 對(duì)象,表示新定義的匿名類將與之關(guān)聯(lián)的上下文。通常,這個(gè)類是匿名類邏輯上的“宿主”類。2.2 classBytes:這是一個(gè)字節(jié)數(shù)組,包含了新匿名類的字節(jié)碼。這些字節(jié)碼通常是通過某種字節(jié)碼生成庫(kù)(如ASM)動(dòng)態(tài)生成的。2.3 null:這個(gè)參數(shù)是用于類定義時(shí)的常量池補(bǔ)丁,這里傳入 null 表示不需要進(jìn)行常量池的補(bǔ)丁。3.關(guān)鍵代碼塊或語句的解釋3.1 UNSAFE:這是 sun.misc.Unsafe 類的一個(gè)實(shí)例。Unsafe 類提供了一組底層、危險(xiǎn)的操作,通常不推薦在標(biāo)準(zhǔn)Java代碼中使用。但在某些特殊場(chǎng)景下,如動(dòng)態(tài)類生成、低級(jí)并發(fā)控制等,Unsafe 提供的功能是必需的。3.2 .defineAnonymousClass(targetClass, classBytes, null):這個(gè)方法調(diào)用是動(dòng)態(tài)定義匿名類的關(guān)鍵。它將 classBytes 中的字節(jié)碼轉(zhuǎn)換為一個(gè)Java類,并將這個(gè)新類與 targetClass 關(guān)聯(lián)起來。由于這個(gè)類是匿名的,它沒有正式的類名。傳入的 null 參數(shù)表示在定義類的過程中不需要對(duì)常量池進(jìn)行任何補(bǔ)丁操作。*/// 通過 Unsafe 類的 defineAnonymousClass 方法動(dòng)態(tài)定義了一個(gè)匿名類,這個(gè)類的字節(jié)碼由 classBytes 提供,而這個(gè)匿名類在邏輯上與 targetClass 關(guān)聯(lián)。return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);}
四、利用java.lang.invoke.InnerClassLambdaMetafactory#dumper 轉(zhuǎn)儲(chǔ)lambda文件
// 靜態(tài)初始化塊,用于初始化dumperstatic {/*** 獲取并設(shè)置代理類轉(zhuǎn)儲(chǔ)功能*/// 定義系統(tǒng)屬性的鍵名,用于控制是否轉(zhuǎn)儲(chǔ)內(nèi)部lambda代理類final String key = "jdk.internal.lambda.dumpProxyClasses";// 使用AccessController執(zhí)行特權(quán)操作,獲取系統(tǒng)屬性值String path = AccessController.doPrivileged(new GetPropertyAction(key), // 創(chuàng)建獲取屬性的動(dòng)作null, // 不指定AccessControlContextnew PropertyPermission(key , "read") // 指定所需的權(quán)限);// 根據(jù)獲取的路徑創(chuàng)建ProxyClassesDumper實(shí)例// 如果路徑為null,則不啟用轉(zhuǎn)儲(chǔ)功能dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);}
JVM參數(shù):jdk.internal.lambda.dumpProxyClasses
命令:java -Djdk.internal.lambda.dumpProxyClasses ClassName
轉(zhuǎn)儲(chǔ)得到內(nèi)部類:
反編譯:java -jar cfr-0.152.jar LambdaExample.class --decodelambdas false
步驟一:源碼
import java.util.Arrays;
import java.util.List;public class LambdaExample {public static void main(String[] args) {List<String> items = Arrays.asList("Apple", "Banana", "Cherry");items.forEach(item -> System.out.println(item));}
}
步驟二:編譯,生成LambdaExample.class 文件
javac LambdaExample.java
步驟三:執(zhí)行java命令,生成文件:LambdaExample$$Lambda$1.class
java -Djdk.internal.lambda.dumpProxyClasses LambdaExample
這個(gè)命令是用來調(diào)試和分析 Java 中 lambda 表達(dá)式的底層實(shí)現(xiàn)的。具體解釋如下:
-
-D
參數(shù):
用于設(shè)置系統(tǒng)屬性。 -
jdk.internal.lambda.dumpProxyClasses
:
這是一個(gè)特殊的系統(tǒng)屬性,用于指示 JVM 將 lambda 表達(dá)式生成的代理類保存到磁盤。 -
LambdaExample
:
這是要運(yùn)行的包含 lambda 表達(dá)式的 Java 類名。
當(dāng)你運(yùn)行這個(gè)命令時(shí),JVM 會(huì)執(zhí)行以下操作:
- 運(yùn)行
LambdaExample
類。 - 對(duì)于該類中的每個(gè) lambda 表達(dá)式,JVM 會(huì)生成一個(gè)代理類。
- 這些生成的代理類會(huì)被保存到磁盤上,通常在當(dāng)前工作目錄下。
這個(gè)功能主要用于:
- 分析 lambda 表達(dá)式的底層實(shí)現(xiàn)
- 調(diào)試復(fù)雜的 lambda 表達(dá)式
- 了解 JVM 如何處理和優(yōu)化 lambda 表達(dá)式
生成的代理類文件名通常遵循這樣的模式:
主類名$Lambda$序號(hào).class
步驟四:生成反編譯代碼:lambda內(nèi)部類
java -jar cfr-0.152.jar 'LambdaExample$$Lambda$1.class' --decodelambdas false
mac電腦,此處LambdaExample$$Lambda$1.class需要帶引號(hào),因?yàn)樵诿钚兄?#xff0c;$ 是一個(gè)特殊字符,用于引用變量。在這個(gè)上下文中,$$ 容易被解釋為當(dāng)前 shell 進(jìn)程的 PID(進(jìn)程ID),而不是文件名的一部分。所以你需要用引號(hào)將文件名括起來,這樣可以防止 shell 解釋 $ 字符。
/** Decompiled with CFR 0.152.*/
import java.lang.invoke.LambdaForm;
import java.util.function.Consumer;final class LambdaExample$$Lambda$1
implements Consumer {private LambdaExample$$Lambda$1() {}@LambdaForm.Hiddenpublic void accept(Object object) {LambdaExample.lambda$main$0((String)object);}
}
這段代碼是由Java編譯器為lambda表達(dá)式生成的內(nèi)部類。讓我們逐部分解析:
-
final class LambdaExample$$Lambda$1
- 這是一個(gè)自動(dòng)生成的內(nèi)部類,名稱中的
$$Lambda$1
表示它是為第一個(gè)lambda表達(dá)式生成的。 final
關(guān)鍵字表示這個(gè)類不能被繼承。
- 這是一個(gè)自動(dòng)生成的內(nèi)部類,名稱中的
-
implements Consumer
- 這個(gè)類實(shí)現(xiàn)了
Consumer
接口,這是Java 8引入的函數(shù)式接口之一。
- 這個(gè)類實(shí)現(xiàn)了
-
private LambdaExample$$Lambda$1()
- 這是一個(gè)私有構(gòu)造函數(shù),防止外部直接實(shí)例化這個(gè)類。
-
@LambdaForm.Hidden
- 這是一個(gè)內(nèi)部注解,用于標(biāo)記這個(gè)方法不應(yīng)該在堆棧跟蹤中顯示。
-
public void accept(Object object)
- 這是
Consumer
接口中定義的方法。 - 方法接受一個(gè)
Object
類型的參數(shù)。
- 這是
-
LambdaExample.lambda$main$0((String)object);
- 這行代碼調(diào)用了
LambdaExample
類中的一個(gè)靜態(tài)方法lambda$main$0
。 - 參數(shù)
object
被強(qiáng)制轉(zhuǎn)換為String
類型。
- 這行代碼調(diào)用了
這個(gè)生成的類實(shí)際上是lambda表達(dá)式的一個(gè)"包裝器"。它將lambda表達(dá)式封裝成一個(gè)實(shí)現(xiàn)了 Consumer
接口的具體類。當(dāng)lambda表達(dá)式被調(diào)用時(shí),它會(huì)調(diào)用 LambdaExample
類中相應(yīng)的靜態(tài)方法(在這里是 lambda$main$0
)。
這種實(shí)現(xiàn)方式允許Java在不使用匿名內(nèi)部類的情況下支持lambda表達(dá)式,從而提高了性能和減少了內(nèi)存使用。
三、結(jié)論
lambda 底層實(shí)現(xiàn)機(jī)制
1.lambda 表達(dá)式的本質(zhì):函數(shù)式接口的匿名子類的匿名對(duì)象
2.lambda表達(dá)式是語法糖
語法糖:編碼時(shí)是lambda簡(jiǎn)潔的表達(dá)式,在字節(jié)碼期,語法糖會(huì)被轉(zhuǎn)換為實(shí)際復(fù)雜的實(shí)現(xiàn)方式,含義不變;即編碼表面有個(gè)糖衣,在編譯期會(huì)被脫掉
Lambda表達(dá)式的編譯及運(yùn)行過程如下:
編譯階段
-
Lambda表達(dá)式識(shí)別:
- 編譯器識(shí)別Lambda表達(dá)式,將其轉(zhuǎn)換為靜態(tài)方法。
-
生成invokedynamic指令:
- 編譯器為每個(gè)Lambda表達(dá)式生成一個(gè)invokedynamic指令。
- 指定LambdaMetafactory.metafactory或altMetafactory作為引導(dǎo)方法。
-
ASM使用:
- 編譯器可能使用ASM庫(kù)生成或修改字節(jié)碼。
運(yùn)行階段
-
引導(dǎo)方法(Bootstrap Method)調(diào)用:
- 當(dāng)JVM首次遇到某個(gè)
invokedynamic
指令時(shí),它會(huì)調(diào)用指定的引導(dǎo)方法。對(duì)于Lambda表達(dá)式,這個(gè)引導(dǎo)方法通常是LambdaMetafactory
的metafactory
方法。
- 當(dāng)JVM首次遇到某個(gè)
-
LambdaMetafactory調(diào)用:
- 引導(dǎo)方法(通常是LambdaMetafactory.metafactory)被調(diào)用。
- 接收參數(shù):MethodHandles.Lookup、函數(shù)式接口信息、Lambda方法信息等。
-
創(chuàng)建
CallSite
對(duì)象:- 引導(dǎo)方法的任務(wù)之一是創(chuàng)建一個(gè)
CallSite
對(duì)象。CallSite
是一個(gè)抽象類,它代表了一個(gè)動(dòng)態(tài)方法調(diào)用點(diǎn)。它的具體實(shí)現(xiàn)類(如ConstantCallSite
)封裝了對(duì)特定方法的調(diào)用。 CallSite
對(duì)象持有一個(gè)MethodHandle
,這個(gè)MethodHandle
指向?qū)嶋H要執(zhí)行的方法。對(duì)于Lambda表達(dá)式,這個(gè)方法是Lambda表達(dá)式轉(zhuǎn)換成的方法。
- 引導(dǎo)方法的任務(wù)之一是創(chuàng)建一個(gè)
-
綁定
MethodHandle
:- 在創(chuàng)建
CallSite
對(duì)象時(shí),引導(dǎo)方法會(huì)根據(jù)Lambda表達(dá)式的目標(biāo)類型和實(shí)際代碼,構(gòu)造一個(gè)MethodHandle
。這個(gè)MethodHandle
直接指向了包含Lambda表達(dá)式代碼的方法。 - 然后,這個(gè)
MethodHandle
被綁定到CallSite
對(duì)象上。這意味著,當(dāng)通過這個(gè)CallSite
調(diào)用方法時(shí),實(shí)際上是通過綁定的MethodHandle
來調(diào)用Lambda表達(dá)式對(duì)應(yīng)的方法。
- 在創(chuàng)建
-
返回
CallSite
對(duì)象:- 引導(dǎo)方法返回
CallSite
對(duì)象給JVM。這個(gè)CallSite
對(duì)象隨后被用于所有對(duì)該invokedynamic
指令的調(diào)用。 - 由于
CallSite
對(duì)象已經(jīng)綁定了對(duì)應(yīng)的MethodHandle
,因此每次通過這個(gè)CallSite
調(diào)用方法時(shí),都會(huì)直接調(diào)用到Lambda表達(dá)式對(duì)應(yīng)的方法,無需再次解析。
- 引導(dǎo)方法返回
-
InnerClassLambdaMetafactory使用:
InnerClassLambdaMetafactory
用于動(dòng)態(tài)生成實(shí)現(xiàn)函數(shù)式接口的類。這個(gè)過程主要通過spinInnerClass
方法實(shí)現(xiàn)。 -
spinInnerClass
方法
spinInnerClass
方法的主要任務(wù)是動(dòng)態(tài)生成一個(gè)類,這個(gè)類實(shí)現(xiàn)了指定的函數(shù)式接口,并包含了Lambda表達(dá)式的代碼。這個(gè)方法通過直接操作字節(jié)碼來創(chuàng)建類,通常使用ASM
庫(kù)來完成。 -
使用
Unsafe
生成匿名類
spinInnerClass
方法可能會(huì)通過Unsafe
類的功能來加載生成的字節(jié)碼。Unsafe
是JDK內(nèi)部的一個(gè)類,提供了一些底層操作,比如直接內(nèi)存訪問、線程調(diào)度等。其中,Unsafe.defineAnonymousClass
方法可以用來加載一個(gè)類的字節(jié)碼,并返回這個(gè)類的Class
對(duì)象。這個(gè)方法允許動(dòng)態(tài)生成的類沒有對(duì)應(yīng)的.class
文件。 -
生成匿名類的過程
-
生成字節(jié)碼:
spinInnerClass
方法使用ASM
庫(kù)生成實(shí)現(xiàn)了函數(shù)式接口的類的字節(jié)碼。這個(gè)類包含了Lambda表達(dá)式的實(shí)現(xiàn)代碼。
-
加載類:
- 使用
Unsafe.defineAnonymousClass
方法加載生成的字節(jié)碼。這個(gè)方法接受三個(gè)參數(shù):父類的Class
對(duì)象、字節(jié)碼數(shù)組、以及與類相關(guān)的常量池補(bǔ)丁。這個(gè)方法返回新加載的類的Class
對(duì)象。
- 使用
-
實(shí)例化:
- 通過反射或其他機(jī)制,使用返回的
Class
對(duì)象創(chuàng)建實(shí)例。這個(gè)實(shí)例實(shí)現(xiàn)了指定的函數(shù)式接口,并包含了Lambda表達(dá)式的代碼。
- 通過反射或其他機(jī)制,使用返回的
-
-
綁定到
CallSite
:- 創(chuàng)建一個(gè)
MethodHandle
,指向新生成的類的實(shí)例方法。這個(gè)MethodHandle
隨后被綁定到CallSite
對(duì)象上,用于后續(xù)的方法調(diào)用。
- 創(chuàng)建一個(gè)
注意
Unsafe
類的使用通常不推薦,因?yàn)樗峁┝撕芏鄰?qiáng)大但危險(xiǎn)的底層操作。在JDK 9及以后版本中,Unsafe
類的一些功能被限制或替換,以促進(jìn)更安全的編程實(shí)踐。- JDK的具體實(shí)現(xiàn)細(xì)節(jié)可能會(huì)隨著版本變化。上述過程主要描述了一種通過
Unsafe
加載動(dòng)態(tài)生成類的方法,但實(shí)際的實(shí)現(xiàn)可能會(huì)有所不同。
- Lambda表達(dá)式執(zhí)行:
- 當(dāng)調(diào)用Lambda表達(dá)式時(shí),通過CallSite間接調(diào)用動(dòng)態(tài)生成的類中的方法。