设计模式之cglib动态代理
什么是动态代理呢?
动态代理就是在java进程运行时,通过字节码技术,动态的生成某个类的代理类。在这个代理类中,我们可以做一些额外的操作,一方面仍然保持原有的方法的能力,另外一方面还增强了这些能力。听着是不是AOP有点像,没错,动态代理就是AOP的技术基石。
在这之前我曾经写过两篇相关的文章:
https://www.cnblogs.com/jilodream/p/10611593.html 设计模式之代理模式
https://www.cnblogs.com/jilodream/p/10624940.html 设计模式之Jdk动态代理
而在实现动态代理时,一般会有两种办法:
jdk动态代理(可以点上文链接查看),以及cglib动态代理,
话不多说,我们直接来看如何使用cglib来动态代理:
例子我们还是使用和jdk动态代理相同的明星/明星代理类这个场景
pom依赖
1 <dependency> 2 <groupId>cglib</groupId> 3 <artifactId>cglib</artifactId> 4 <version>3.1</version> 5 </dependency>
明星类
1 packagecom.example.demo.learncglib;2 3 importlombok.Data;4 importlombok.NoArgsConstructor;5 6 /** 7 * @discription8 */ 9 10 @Data11 @NoArgsConstructor12 public class SuperStar implementsISuperStar {13 String starName;14 15 publicSuperStar(String starName) {16 this.starName =starName;17 }18 19 @Override20 public voidsignContract() {21 System.out.println(starName + " 签名");22 //to do sth 23 return;24 }25 26 @Override27 public voidnegotiate() {28 System.out.println(starName + " 谈判");29 //to do sth 30 return;31 }32 33 @Override34 publicString getStarImage() {35 System.out.println(starName + " start image");36 //to do sth 37 return "One " + starName + " Image";38 }39 }
明星类接口
1 packagecom.example.demo.learncglib;2 3 public interfaceISuperStar4 {5 /** 6 * 签约7 */ 8 voidsignContract();9 10 voidnegotiate();11 12 String getStarImage();13 }
代理工厂
1 packagecom.example.demo.learncglib;2 3 4 importnet.sf.cglib.core.DebuggingClassWriter;5 importnet.sf.cglib.proxy.Enhancer;6 importnet.sf.cglib.proxy.MethodInterceptor;7 importnet.sf.cglib.proxy.MethodProxy;8 9 importjava.lang.reflect.Method;10 11 /** 12 * @discription13 */ 14 public class ProxyFactory implementsMethodInterceptor {15 16 privateString starName;17 18 publicSuperStar create(String starName) {19 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code\\common\\learn-design" +
20 "-pattern\\target\\cglib");21 22 this.starName =starName;23 Enhancer enhancer = newEnhancer();24 enhancer.setSuperclass(SuperStar.class);25 enhancer.setCallback(this);26 SuperStar proxy =(SuperStar) enhancer.create();27 proxy.starName =starName;28 returnproxy;29 }30 31 32 @Override33 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throwsThrowable {34 System.out.println(starName + "的代理人开始组织活动");35 Object obj =methodProxy.invokeSuper(o, objects);36 System.out.println(starName + "的代理人结束组织活动");37 returnobj;38 }39 }
主类
1 packagecom.example.demo.learncglib;2 3 /** 4 * @discription5 */ 6 public classCglibMain {7 public static voidmain(String[] args) {8 ProxyFactory factory = newProxyFactory();9 SuperStar superStar = factory.create("messi");10 superStar.signContract();11 superStar.negotiate();12 String image=superStar.getStarImage();13 System.out.println("we get a image: "+image);14 }15 16 17 }
运行效果
messi的代理人开始组织活动
messi 签名
messi的代理人结束组织活动
messi的代理人开始组织活动
messi 谈判
messi的代理人结束组织活动
messi的代理人开始组织活动
messi start image
messi的代理人结束组织活动
we get a image: One messi Image
Disconnected from the target VM, address:'127.0.0.1:64165', transport: 'socket'
生成代理类的核心逻辑在com.example.demo.learncglib.ProxyFactory#create方法中:
我们首先声明一个增强器:Enhancer enhancer
接着设置代理类父类:SuperStar.class
接着设置回调类(包含增强方法的类):? implements MethodInterceptor
最后调用增强类的创建方法就生成好了:enhancer.create()
整体的流程和jdk动态代理很像,
不同点是jdk动态代理是根据接口,动态的生成实现类,代理类和被代理类均为接口实现类,是“兄弟关系”,被代理的方法就是接口中公开的方法。
而cglib动态代理是将被代理类作为父类,派生出子类,代理类和被代理类均为继承关系,是“父子关系”,被代理的方法就是父类中公开的方法。
如下图:
其实细细想想也很正常,我们想要间接的动态的获取到某个类中公开的方法,有两种途径,第一种是继承自它,那么它所有的公开方法我们都能继续持有(cglib的思路)。第二种就是和他实现相同的约定(接口),那么它多有开发的协议,我们也就能动态的获取到了(jdk动态代理的思路)。
说了这么多来讲讲cglib方式的局限性,主要还是和继承有关系:
1、无法动态代理final方法,因为子类无法复写。
2、无法动态代理static,因为子类无法复写。
jdk动态代理和cglib可以说是各有优劣,很多人说经过jdk动态代理的速度优化,spring 目前已经默认采用jdk动态代理了,所以jdk动态代理更好。
这里我要说两点,
1、设计和实现是两回事,未来cglib的实现思路经过优化,又胜出了,也并不能说明cglib的设计方案更好;
2、目前的Springboot最新版本又采用了cglib作为默认的aop实现方式,这也并不能说明cglib就比jdk动态代理方式要强了。
ps:如果想要将运行时动态生成的class文件保存到磁盘中,可以在执行的代码出添加,如上文代码示例的红色字体处:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code\\common\\learn-design-pattern\\target\\cglib");