設計模式之cglib動態代理

王若伊_恩赐解脱發表於2024-08-20

什麼是動態代理呢?
動態代理就是在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");

相關文章