- 反射:https://www.cnblogs.com/yougewe/p/10125073.html
将Java文件保存到本地硬盘后,编译Java文件,生成.class文件,然后使用JVM将字节码文件加载到内存,字节码文件在内存(方法区)中生成一个Class类表示此Java对象
使用反射的时候,首先获取到Class类,这样就可以得到class文件里的所有内容,包含属性、构造方法、普通方法;
属性通过Filed类表示,构造方法通过Constructor表示,普通方法通过Method表示
Class.forName(name):先获取ClassLoader, 然后调用native方法获取Class信息,最后,jvm会回调 ClassLoader 进行类的加载
newInstance():主要就是拿到无参的构造器进行调用,而获取匹配的构造器则分为三步:
1 先获取所有的constructors, 然后通过进行参数类型比较
2 找到匹配后,通过 ReflectionFactory copy一份constructor返回,否则抛出 NoSuchMethodException
class.getDeclaredMethod(nam):获取所有方法列表,根据要调用的方法名称,去方法列表里选出匹配的方法即可,如果没有则抛异常
Method.invoke(Object obj, Object… args):通过native的invoke0()执行;
method.invoke()方法支持多态特性,其native实现在方法真正执行之前通过动态连接或者虚方法表来实现:https://blog.csdn.net/wenyuan65/article/details/81145900,总体来说就是:
当反射调用的方法是接口方法时,调用Reflection::resolve_interface_call(),该方法依赖LinkResolver::resolve_interface_call()来完成方法的动态链接过程
当反射调用的方法是可以被覆盖的方法,例如Animal.print(), Reflection::invoke()最终通过查询虚方法表vtable来确定最终的method。
(针对方法调用动态分派的过程,虚拟机会在类的方法区建立一个虚拟方法表的数据结构(virtual method table,vtable),
针对于invokeinterface指令来说,虚拟机会建立一个叫做接口方法表的数据结构(interface method table,itable):https://blog.csdn.net/wzq6578702/article/details/82712667)
方法的反射调用会带来不少性能开销,原因主要有三个:
1 变长参数方法导致的 Object 数组;
2 基本类型的自动装箱、拆箱;
3 在生产环境中往往拥有多个不同的反射调用,对应多个 GeneratedMethodAccessor,也就是动态实现,由于 JVM关于调用点(Method.invoke)的类型 profile(注:对于invokevirtual
或者invokeinterface
,Java 虚拟机会记录下调用者的具体类型,称之为类型 profle)无法同时记录这么多个类,因此可能造成所测试的反射调用没有被内联的情况,也就是说无法进行方法内联
(方法内联指的是编译器在编译一个方法时,将某个方法调用的目标方法也纳入编译范围内,并用其返回值替代原方法调用,也就是把函数调用的方法直接内嵌到方法内部,减少函数调用的次数 )
反射的优化:Object 数组不能存储基本类型,Java 编译器会对传入的基本类型参数进行自动装箱,因此可以扩大缓存的范围;每次反射调用都会检查目标方法的权限,这个检查可以在 Java 代码里关闭;可以关闭反射调用的 Infation 机制,从而取消委派实现,并且直接使用动态实现
- JDK动态代理:https://www.cnblogs.com/zuidongfeng/p/8735241.html
为业务接口创建代理类的字节码文件,使用ClassLoader将字节码文件加载到JVM,然后获取此类的Class对象,使用其构造函数,传入InvocationHandler接口的实现类,通过反射来创建此代理类,最终返回给使用者:
return cons.newInstance(new Object[]{h});
查看此类,可以看到此代理类(命名如
$Proxy0
)继承自Proxy,实现了业务接口,将实现了InvocationHandler接口的类组合进Proxy,会生成业务接口的所有方法,这些方法的内部都大致如下:super.h.invoke(this, m3, (Object[])null);//m3是Method类型的变量,为接口的方法
即调用Proxy内部的InvocationHandler接口实现类的invoke()方法,所以我们只需要在InvocationHandler的实现类中组合进业务接口的实现类,调用此实现类的方法即可:
private Object target;//target即为传进来的业务接口实现类 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ System.out.println("Do something before"); Object result = method.invoke(target, args); System.out.println("Do something after"); return result; }
- CGlib动态代理
create()方法里有一行代码会生成代理对象的字节码:
byte[] b = strategy.generate(this);
根据此字节码生成的代理对象(命名如xxxService$$EnhancerByCGLIB$$4da4ebaf
)会继承自业务类,重写了业务类的方法,重写的方法内部都会调用MethodInterceptor接口实现类的intercept()方法,此方法可通过MethodProxy来调用父类(即业务类)的方法;
创建的此代理对象一般会被赋值给业务类,那么就是“父类引用指向子类对象”,调用方法时,会调用被代理类重写的方法,也就达到了代理的目的
- lambda
编译器会为每一个lambda表达式生成一个方法,方法名是lambda$0,1,2,3,但方法引用的表达式不会生成方法。
在出现lambda的地方会产生一个invokeDynamic指令,这个指令会调用bootstrapMethod(),bootstrapMethod()在源码中并不存在,是由INDY产生的,调用了MethodHandlers$Lookup的findStatic()方法,产生要调用的方法的MethodHandler,然后使用这个MethodHandlers传给LambdaMetafactory.metafactory静态方法,此方法会创建一个ConstantCallSite对象,最后将这个对象返回给invokedynamic指令,,实现了调用(ConstantCallSite里面包含了MethodHandler,也就是最终调用的方法。)
-
注解:https://www.cnblogs.com/throwable/p/9747595.html
注解最后转化为一个接口,注解中定义的注解成员属性会转化为抽象方法;
然后JVM会通过动态代理生成一个实现了"注解对应接口"的实例,该代理类实例实现了"注解成员属性对应的方法",这个步骤类似于"注解成员属性"的赋值过程,这样子就可以在程序运行的时候通过反射获取到注解的成员属性 -
Stream
通过pipeline来完成,本质是一个拦截器链的设计