Java中分为静态代理、动态代理,其中动态代理又分为jdk动态代理和cglib代理
jvm的类加载机制 中的加载阶段要做的三件事:
1.
通过一个类的全名或其它途径来获取这个类的二进制字节流
2.
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3.
在内存中生成一个代表这个类的 Class 对象, 作为方法区中对这个类访问的入口
jdk动态代理:
在 Java 的动态代理中, 主要涉及 2 个类,java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler 我们需要一个实现 InvocationHandler 接口的中间类, 这个接口只有一个方法 invoke 方法, 方法的每个参数的注释如下代码。
我们对处理类中的所有方法的调用都会变成对 invoke 方法的调用,这样我们可以在 invoke 方法中添加统一的处理逻辑(也可以根据 method 参数判断是哪个方法)。中间类 (实现了 InvocationHandler 的类) 有一个委托类对象引用, 在 Invoke 方法中调用了委托类对象的相应方法,通过这种聚合的方式持有委托类对象引用,把外部对 invoke 的调用最终都转为对委托类对象的调用。
实际上,中间类与委托类构成了静态代理关系,在这个关系中,中间类是代理类,委托类是委托类。然后代理类与中间类也构成一个静态代理关系,在这个关系中,中间类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。
CGLIB 创建动态代理类的模式是:
1.
查找目标类上的所有非 final 的 public 类型的方法 (final 的不能被重写)
2.
将这些方法的定义转成字节码
3.
将组成的字节码转换成相应的代理的 Class 对象然后通过反射获得代理类的实例对象
4.
实现 MethodInterceptor 接口, 用来处理对代理类上所有方法的请求
总结:静态代理比较好理解,需要被代理的类和代理类实现用一个接口,然后在代理类中调用真正实现类,并且静态代理的关系在编译器就已经确定了,而动态代理的关系是现在运行期间确定的。静态代理实现简单,适合于代理类较少的且确定的情况,而动态代理则给我们提供更大的灵活性。
jdk动态代理所用到的代理类在程序调用到代理类对象时才由jvm创建,jvm根据传进来的业务实现类对象以及方法名,动态创建一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用,我们做的只需要指定代理类的预处理、调用后操作即可。
静态代理和jdk动态代理都是基于接口实现的,对于那些没有提供接口只是提供实现类而言,就只能选择cglib动态代理。
描述代理的几种实现方式?分别说出优缺点?
1、静态代理:
代理对象和实际对象都继承同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是实际对象。
优点:可以很好的保护实际对象的业务逻辑,从而提高安全性
缺点:不同的接口要有不同的代理类实现,会造成代码冗余。
2、jdk动态代理:
为了解决静态代理中 生成大量的代理类造成的冗余
jdk动态代理类只需要实现IncocationHandler接口,重写invoke方法便可以完成代理的实现。
jdk动态代理是利用反射生成代理类Proxyxxx.class代理类字节码,并生成对象
jdk动态代理之所以只能代理接口是因为代理类本身就继承了Proxy,而java是不允许多重继承的,但是允许实现多个接口。
优点:解决了静态代理冗余代理类的问题
缺点:jdk动态代理是基于接口设计实现的,如果没有接口会抛出异常。
3、cglib动态代理
由于jdk动态代理只能基于接口设计,而对于没有接口的情况,jdk方式解决不了,cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现,实现方式是实现 MethodInterceptor接口,重写intercept方法,通过Enhhancer类的回调方法来实现
但是cglib在创建代理对象所花的时间比jdk多的多,所以对于单例的对象,因为无需频繁创建对象,用cglib合适,反之用jdk方式更合适。
优点:没有接口也能实现动态代理,采用字节码增强技术,性能也不错
缺点:技术实现相对难理解。
jdk动态代理和cglib动态代理的区别?
jdk动态代理基于Java反射机制实现,必须要实现了接口的业务类才能用这中方法生成代理对象。
cglib代理基于ASM框架通过生成业务类的子类来实现。
jdk动态代理的优势是最小依赖化,减少依赖意味着简化开发和维护并且jdk自身支持,还可平滑进行jdk版本升级,代码实现简单。基于cglib框架的优势是无需实现接口,达到代理无侵入,我们只需要操作我们关系的类,不必为其它相关类增加工作量,性能比较高。