动态代理笔记

以前我眼里动态代理一直是个很神秘的东西,面试一旦问到必然摇头回答不知道

想办法理一下,目前动态代理有使用JDK类库 和使用CGLIB 两种方式

JDK类库的大概用法是这样的:

写一个类 写一个接口 叫MyClassMyClassImpl

写一个Handler类,实现InvocationHandler这个JDK反射包里的接口,构造器传入一个MyClass的实例,并且覆盖invoke方法,然后在invoke方法中实现具体的逻辑

如果要使用动态代理,就需要使用JDK的Proxy.newProxyInstance()方法,传入类加载器、代理需要实现的接口、你写的Handler类的一个新实例,并且传入一个MyClassImpl作为这个新实例的构造函数参数。

就可以在方法调用时 动态生成Proxy类,并且实际执行Handler中的逻辑了。

这一套看起来很眼熟,Spring的AOP相关类库 就是对使用动态代理的简化。


但是JDK动态代理 要求被代理的类必须实现接口,碰到没有接口的类 就必须使用CGLIB来操作字节码了。

其实和JDK库类似,只不过实际使用的类库不太一样:

首先写一个MyClass类,再写一个Interceptor类 实现MethodInterceptor这个CGLIB的接口,覆盖intercept方法,然后在intercept方法里实现具体的逻辑

而实际使用动态代理时,new 一个 Enhancer对象(CGLIB提供这个类),设置要代理的类和Interceptor对象,再使用create()方法创建被代理后的对象。

这样做,所有非final方法都会被代理。而由于生成的代理类是继承了原类的,所以final类被代理时会抛出异常。

对于Object类,JDK只代理hashCode()equals()toString(),按官方文档的说法,是“和接口方法一样处理”;

而CGLIB则是代理wait(),notify()notifyAll(),getClass()四个方法,因为它们是final的。

面经读后感-类加载顺序

讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字

段,当new的时候,他们的执行顺序。

死板答案:父类静态字段/静态块 子类静态字段/静态块 父类字段/代码块 父类构造器 子类字段/代码块 子类构造器

解释:
编译器会收集所有字段赋值和静态块 搞成一个方法,顺序由源码顺序决定,并且保证父类的比子类的先执行。
但是常量赋值会在之前执行。
所以必然是父类的静态字段/块,子类静态字段/块。
然后是方法,初始化所有字段,并且调用其他方法。
分几种情况:
如果对应的源码构造器内明确从调用另一个构造器开始,则该方法会先调用对应的方法。
否则该方法会调用父类的方法,初始化字段,然后是该方法本体的字节码)