对于需要被代理的类,它只是动态生成一个子类以覆盖非final的方法,同时绑定钩子回调自定义的拦截器。它比JDK动态代理在性能上要快。值得注意的是,我们传入目标类作为代理类的父类。不同于JDK动态代理,我们不能使用目标对象来创建代理。目标对象只能被CGLIB创建。在例子中,默认的无参构造方法被使用来创建目标对象。
总结
1.静态代理比较容易理解,需要被代理的类和代理类实现自同一个接口,然后在代理类中调用真正的实现类,并且静态代理的关系在编译期间就已经确定了。而动态代理的关系是在运行期间确定的。静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性。
2.JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的业务实现类对象以及方法名,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的只是指定代理类的预处理、调用后操作即可。
3.静态代理和动态代理都是基于接口实现的,而对于那些没有提供接口只是提供了实现类的而言,就只能选择CGLIB动态代理了。
面试题1:JDK动态代理和CGLIB动态代理的区别
1.JDK动态代理是基于Java反射实现的,必须要实现了接口的业务类才能用这种方法生成代理对象。
2.CGLIB动态代理是基于ASM框架,通过生成业务类的子类来实现的。
3.JDK动态代理的优势是最小化依赖关系,减少依赖意味着简化开发和维护,并且有JDK自身的支持。还可以平滑地进行JDK版本升级,代码实现简单。
4.基于CGLIB框架的优势是无须实现接口,达到代理类无侵入,我们只需操作我们关心的类,不必为其他相关类增加工作量,性能比较高。
面试题2:描述代理的几种实现方式,并说出优缺点
代理可以分为静态代理和动态代理,而动态代理又分为JDK动态代理和CGLIB动态代理。
静态代理:代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是Real Object。优点是可以很好地保护实际对象的业务逻辑对外暴露,从而提高安全性。缺点是不同的接口要有不同的代理类实现,会冗余。
JDK动态代理:JDK动态代理只需要实现InvocationHandler接口,重写invoke()方法便可以完成代理的实现,原理是利用反射生成代理类Proxyxx.class代理类字节码并生成对象。JDK动态代理之所以只能代理接口是因为代理类本身已经继承了Proxy类,而Java是不允许多重继承的,只是允许实现多个接口。优点是解决了静态代理中冗余的代理实现类问题。缺点是JDK动态代理是基于接口的设计实现的,如果没有接口就会抛异常。
CGLIB动态代理:由于JDK动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK方式解决不了。CGLIB采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑来完成动态代理的实现。实现的具体方式是实现MethodInterceptor接口并重写intercept()方法,通过Enhancer类的回调方法来实现。但是CGLIB在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象因为无需频繁创建对象用CGLIB更合适。反之则是使用JDK动态代理的方式更合适一些。同时,由于CGLIB是采用的动态创建子类的方法,对于final方法是无法进行代理的。优点是没有接口也能够实现动态代理,而且采用的是字节码增强技术,性能也不错。缺点则是技术实现上要相对难理解些。