前语
java中有lambda,在运用java的过程中咱们没少用(啥?用的kotlin?你别说话)但是你知道lambda的完成原理吗?
接下来就来解析lambda完成,不过在lambda之前咱们与一个了解的老伙计谈谈心————匿名类,为什么因为他们有点相似.
匿名类的完成办法
从字节码的层面上来说new接口和new笼统类是极端笼统且不合理的。
比如这样。
public class Test {
public static void main(String[] args) {
new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
}.run();
}
}
当咱们对这个文件进行编译今后会得到两个文件
Test.class以及Test$1.class
运用jclasslib idea插件对Test.class文件解析今后会发现,我这儿new了另外一个东西Test$1,这是个什么?
反编译今后便是这玩意
所以new接口和new笼统类这种办法并不会削减类的创立,只不过这个完成类是编译器在编译的时分主动帮助创立。而且这个匿名类的称号是有一定规则的。
小结
匿名类的完成即编译器静态生成一个类,new笼统的完成都是引证的详细的匿名类。
Lambda表达式完成
Lambda和匿名类的完成相似,他们的联系就相似于静态署理和动态署理。
匿名类是静态生成,而Lambda是动态生成。
何以见得?show code
public class Test {
public static void main(String[] args) {
Runnable run = () -> System.out.println("Hello world")
run.run();
}
}
这种完成只能经过字节码进行剖析。因为他是最直观的办法
这下还没点开就觉得有些猫腻了。hh,这也算是一个伏笔吧。
剧透一下
这边这个lambda是咱们lambda内部的详细代码.
lambda完成实践是经过asm进行class的生成,然后这个生成的class内部的办法调用了lambdamainmain0.
main字节码剖析
0 invokedynamic #2 <run, BootstrapMethods #0>
5 astore_1
6 aload_1
7 invokeinterface #3 <java/lang/Runnable.run : ()V> count 1
12 return
短短几行代码,我却难以读懂…
invokedynamic
invokedynamic是关键。
他调用了常量池2号常量,也便是一个invokeDynamic
而这个常量链接了一个描述符和一个Bootstrap办法。
这个办法有些长啊
但是这是剖析的关键
这是bootstrp办法的详细签名
<java/lang/invoke/LambdaMetafactory.metafactory :(Ljava/lang/invoke/MethodHandles$Lookup;
Ljava/lang/String;
Ljava/lang/invoke/MethodType;
Ljava/lang/invoke/MethodType;
Ljava/lang/invoke/MethodHandle;
Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;>
等价于
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
也便是说invokedynamic就相当于调用了这个办法
这代码呢也不长
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
就3行,第2行是一些参数的装备不太重要,第3行调用的办法是一个笼统。
所以这样来看InnerClassLambdaMetafactory是突破口
小细节
留意其static代码块
static {
final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
// 这儿获取了一个System property。看这变量称号能猜出他是一个路径
String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
//假如dumpPath为null dumper便是null,否者会生成一个dumper
dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
//这儿也是做一个装备,不过作为过来人告知你这个不是很重要。
final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
disableEagerInitialization = AccessController.doPrivileged(
new GetBooleanAction(disableEagerInitializationKey)).booleanValue();
结构函数
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
MethodType invokedType,
String samMethodName,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType,
boolean isSerializable,
Class<?>[] markerInterfaces,
MethodType[] additionalBridges)
throws LambdaConversionException {
//参数装备不重要
super(caller, invokedType, samMethodName, samMethodType,
implMethod, instantiatedMethodType,
isSerializable, markerInterfaces, additionalBridges);
implMethodClassName = implClass.getName().replace('.', '/');
implMethodName = implInfo.getName();
implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
constructorType = invokedType.changeReturnType(Void.TYPE);
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
//属于是核心逻辑了
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
int parameterCount = invokedType.parameterCount();
if (parameterCount > 0) {
argNames = new String[parameterCount];
argDescs = new String[parameterCount];
for (int i = 0; i < parameterCount; i++) {
argNames[i] = "arg$" + (i + 1);
argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
}
} else {
argNames = argDescs = EMPTY_STRING_ARRAY;
}
}
接着便是最核心的逻辑了
buildCallSite
CallSite buildCallSite() throws LambdaConversionException {
final Class<?> innerClass = spinInnerClass();
...
}
第一行代码今后就生成了相应的class文件
spinInnerClass
代码挺长的
private Class<?> spinInnerClass() throws LambdaConversionException {
String[] interfaces;
String samIntf = samBase.getName().replace('.', '/');
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
//参数装备
...
//利用asm ClassWriter直接生成class字节码
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
lambdaClassName, null,
JAVA_LANG_OBJECT, interfaces);
// Generate final fields to be filled in by constructor
for (int i = 0; i < argDescs.length; i++) {
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
argNames[i],
argDescs[i],
null, null);
fv.visitEnd();
}
generateConstructor();
if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
generateFactory();
}
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
samMethodType.toMethodDescriptorString(), null, null);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
if (additionalBridges != null) {
for (MethodType mt : additionalBridges) {
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
mt.toMethodDescriptorString(), null, null);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
new ForwardingMethodGenerator(mv).generate(mt);
}
}
...
cw.visitEnd();
//转化为字节码
final byte[] classBytes = cw.toByteArray();
//假如dumper不为空就将class字节码文件输出到指定路径
if (dumper != null) {
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
dumper.dumpClass(lambdaClassName, classBytes);
return null;
}
}, null,
new FilePermission("<<ALL FILES>>", "read, write"),
// createDirectories may need it
new PropertyPermission("user.dir", "read"));
}
//定义class
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
}
字节码剖析
运行今后能够发现生成了一个class文件
然而这个class完成了对应的接口,并调用了生成的lambda办法
所以lambda表达式的完成原理就简单了,经过asm生成一个类动态地指向咱们需求履行的代码块所对应的办法。