什麼是動態代理呢?
動態代理就是在java程序執行時,透過位元組碼技術,動態的生成某個類的代理類。在這個代理類中,我們可以做一些額外的操作,一方面仍然保持原有的方法的能力,另外一方面還增強了這些能力。聽著是不是AOP有點像,沒錯,動態代理就是AOP的技術基石。
在這之前我曾經寫過兩篇相關的文章:
https://www.cnblogs.com/jilodream/p/10611593.html 設計模式之代理模式
https://www.cnblogs.com/jilodream/p/10624940.html 設計模式之Jdk動態代理
而在實現動態代理時,一般會有兩種辦法:
jdk動態代理(可以點上文連結檢視),(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )以及cglib動態代理,
話不多說,我們直接來看如何使用cglib來動態代理:
例子我們還是使用和jdk動態代理相同的明星/明星代理類這個場景
pom依賴
1 <dependency> 2 <groupId>cglib</groupId> 3 <artifactId>cglib</artifactId> 4 <version>3.1</version> 5 </dependency>
明星類
1 package com.example.demo.learncglib; 2 3 import lombok.Data; 4 import lombok.NoArgsConstructor; 5 6 /** 7 * @discription 8 */ 9 10 @Data 11 @NoArgsConstructor 12 public class SuperStar implements ISuperStar { 13 String starName; 14 15 public SuperStar(String starName) { 16 this.starName = starName; 17 } 18 19 @Override 20 public void signContract() { 21 System.out.println(starName + " 簽名"); 22 // to do sth 23 return; 24 } 25 26 @Override 27 public void negotiate() { 28 System.out.println(starName + " 談判"); 29 // to do sth 30 return; 31 } 32 33 @Override 34 public String getStarImage() { 35 System.out.println(starName + " start image"); 36 // to do sth 37 return "One " + starName + " Image"; 38 } 39 }
明星類介面
1 package com.example.demo.learncglib; 2 3 public interface ISuperStar 4 { 5 /** 6 * 簽約 7 */ 8 void signContract(); 9 10 void negotiate(); 11 12 String getStarImage(); 13 }
代理工廠
1 package com.example.demo.learncglib; 2 3 4 import net.sf.cglib.core.DebuggingClassWriter; 5 import net.sf.cglib.proxy.Enhancer; 6 import net.sf.cglib.proxy.MethodInterceptor; 7 import net.sf.cglib.proxy.MethodProxy; 8 9 import java.lang.reflect.Method; 10 11 /** 12 * @discription 13 */ 14 public class ProxyFactory implements MethodInterceptor { 15 16 private String starName; 17 18 public SuperStar 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 = new Enhancer(); 24 enhancer.setSuperclass(SuperStar.class); 25 enhancer.setCallback(this); 26 SuperStar proxy = (SuperStar) enhancer.create(); 27 proxy.starName = starName; 28 return proxy; 29 } 30 31 32 @Override 33 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 34 System.out.println(starName + "的代理人開始組織活動"); 35 Object obj = methodProxy.invokeSuper(o, objects); 36 System.out.println(starName + "的代理人結束組織活動"); 37 return obj; 38 } 39 }
主類
1 package com.example.demo.learncglib; 2 3 /** 4 * @discription 5 */ 6 public class CglibMain { 7 public static void main(String[] args) { 8 ProxyFactory factory = new ProxyFactory(); 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(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )方法中:
我們首先宣告一個增強器: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檔案儲存到磁碟中,(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )可以在執行的程式碼出新增,如上文程式碼示例的紅色字型處:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code\\common\\learn-design-pattern\\target\\cglib");