在 Java 開發中,代理模式是一種重要的設計模式,通過代理對象來控制對目標對象的訪問。代理模式在 AOP(面向切面編程)中得到了廣泛應用,尤其是在 Spring 框架中。Spring 提供了兩種主要的代理機制:JDK 動態代理和 CGLIB 動態代理。其中,JDK 動態代理僅能代理實現了接口的類,而 CGLIB 動態代理則沒有這一限制,可以代理任何普通的類。因此,CGLIB 動態代理在實際開發中非常實用,特別是在需要代理沒有實現接口的類時。
本文將深入探討如何在 Spring Boot 3.3 中使用 CGLIB 實現動態代理。我們將通過具體的代碼示例,展示如何在應用程序中集成 CGLIB,并解釋其在 AOP 編程中的應用場景和優勢。同時,我們還將展示如何通過前后端協作,將代理后的效果展示在 Web 頁面上,從而幫助開發者更好地理解和運用 CGLIB 動態代理。
CGLIB(Code Generation Library)是一個強大的高性能代碼生成庫,主要用于在運行時動態生成類和代理對象。CGLIB 通過使用底層的 ASM 字節碼操縱框架,直接操作字節碼文件,生成新的類或增強現有的類。與 JDK 動態代理不同,CGLIB 不需要目標類實現任何接口,這使得它在處理代理普通類時顯得非常靈活和強大。
CGLIB 動態代理的工作原理是通過生成目標類的子類,并在子類中重寫目標類的方法來實現對方法調用的攔截。CGLIB 可以在方法調用的前后添加自定義邏輯,例如日志記錄、性能監控、事務管理等。這使得它在實現 AOP 編程時具有極大的優勢,尤其是在 Spring 框架中被廣泛應用。
值得注意的是,由于 CGLIB 是通過繼承的方式實現代理,因此目標類不能是 final 的,否則會導致代理失敗。此外,目標類中的 final 方法也無法被代理,因為 final 方法不能被重寫。
運行效果:
圖片
若想獲取項目完整代碼以及其他文章的項目源碼,且在代碼編寫時遇到問題需要咨詢交流,歡迎加入下方的知識星球。
在開始之前,我們需要設置一個 Spring Boot 3.3 項目。項目結構如下:
cglib-demo├── src│ ├── main│ │ ├── java│ │ │ └── com│ │ │ └── icoderoad│ │ │ └── cglib│ │ │ ├── service│ │ │ │ └── CglibDemoService.java│ │ │ ├── proxy│ │ │ │ └── CglibProxy.java│ │ │ └── CglibDemoApplication.java│ │ └── resources│ │ ├── application.yaml│ │ └── templates│ │ └── index.html└── pom.xml
首先,在 pom.xml 文件中引入必要的依賴:
<?xml versinotallow="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.icoderoad</groupId> <artifactId>cglib-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cglib-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- CGLIB Dependency --> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.3.0</version> <!-- 或者更高的版本 --> </dependency> <!-- Bootstrap CSS --> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>5.3.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
在 src/main/resources/application.yaml 文件中,我們可以加入一些簡單的配置:
server: port: 8080spring: thymeleaf: cache: false
首先,我們創建一個服務類 CglibDemoService,這個類將被代理:
package com.icoderoad.cglib_demo.service;public class CglibDemoService { public String sayHello(String name) { return "你好, " + name; } public String sayGoodbye(String name) { return "再見, " + name; }}
接下來,我們創建一個 CGLIB 代理類 CglibProxy,用于攔截方法調用并進行處理:
package com.icoderoad.cglib_demo.proxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor { // 被代理的目標對象 private final Object target; // 構造方法,傳入目標對象 public CglibProxy(Object target) { this.target = target; } // 攔截方法,在目標方法執行前后加入自定義邏輯 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("方法執行前: " + method.getName()); Object result = proxy.invoke(target, args); System.out.println("方法執行后: " + method.getName()); return result; } // 獲取代理對象 public Object getProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); }}
在應用的啟動類中,我們將使用 CglibProxy 來代理 CglibDemoService:
package com.icoderoad.cglib_demo;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import com.icoderoad.cglib_demo.proxy.CglibProxy;import com.icoderoad.cglib_demo.service.CglibDemoService;@SpringBootApplicationpublic class CglibDemoApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(CglibDemoApplication.class, args); } @Override public void run(String... args) throws Exception { CglibDemoService targetService = new CglibDemoService(); CglibProxy proxy = new CglibProxy(targetService); CglibDemoService proxyService = (CglibDemoService) proxy.getProxy(); // 調用代理對象的方法 System.out.println(proxyService.sayHello("小明")); System.out.println(proxyService.sayGoodbye("小明")); }}
在這個例子中,我們通過 CglibProxy 代理 CglibDemoService,并在方法調用前后添加了自定義邏輯。
為了將數據傳遞到前端頁面,我們需要創建一個控制器:
package com.icoderoad.cglib_demo.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import com.icoderoad.cglib_demo.proxy.CglibProxy;import com.icoderoad.cglib_demo.service.CglibDemoService;@Controllerpublic class DemoController { @GetMapping("/") public String index(Model model) { // 創建目標對象 CglibDemoService demoService = new CglibDemoService(); // 創建代理對象 CglibProxy proxy = new CglibProxy(demoService); CglibDemoService proxyService = (CglibDemoService) proxy.getProxy(); // 將方法調用結果傳遞給前端頁面 model.addAttribute("helloMessage", proxyService.sayHello("路條編程")); model.addAttribute("goodbyeMessage", proxyService.sayGoodbye("路條編程")); return "index"; }}
在 src/main/resources/templates/index.html 文件中,創建一個簡單的前端頁面:
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="UTF-8"> <title>CGLIB 代理演示</title> <link rel="stylesheet" th:href="@{/webjars/bootstrap/5.3.0/css/bootstrap.min.css}"></head><body><div class="container"> <h1>CGLIB 代理演示</h1> <p th:text="'歡迎消息: ' + ${helloMessage}"></p> <p th:text="'告別消息: ' + ${goodbyeMessage}"></p></div><script th:src="@{/webjars/bootstrap/5.3.0/js/bootstrap.bundle.min.js}"></script></body></html>
在啟動你的應用時,添加 --add-opens 參數以允許訪問被封閉的模塊:
java --add-opens java.base/java.lang=ALL-UNNAMED -jar your-application.jar
如果你是在 IDE 中運行應用程序,可以在 IDE 的運行配置中添加這個參數。
在 Eclipse 中配置 JVM 參數來解決 CGLIB 與 Java 模塊系統兼容性問題,可以按照以下步驟操作:
在左側面板中,選擇 Run/Debug Settings。
選擇或創建運行配置
如果已有運行配置,選擇你要修改的配置,然后點擊 Edit(編輯)。
如果沒有,點擊 New Configuration(新建配置),然后選擇 Java Application 或 Spring Boot App,點擊 New(新建)。
配置 VM Arguments
在 Arguments 標簽頁中,找到 VM arguments 輸入框。在這里你可以添加 JVM 啟動參數。
在 VM arguments 輸入框中,添加如下參數:
--add-opens java.base/java.lang=ALL-UNNAMED
這個參數允許你訪問 Java 內部 API,解決 CGLIB 在模塊系統中的兼容性問題。
保存配置
點擊 Apply(應用),然后點擊 Run(運行)以保存并應用你的配置。
啟動 Spring Boot 項目后,訪問 http://localhost:8080,頁面上將顯示通過 CGLIB 動態代理處理后的消息,控制臺中可以看到方法執行前后的日志輸出。
本文詳細介紹了如何在 Spring Boot 3.3 中使用 CGLIB 實現動態代理。通過實際的代碼示例,展示了 CGLIB 在動態代理中的應用,以及如何在 Spring Boot 項目中集成 CGLIB。我們還演示了如何通過 Thymeleaf 和 Bootstrap 實現一個簡單的前端頁面,以展示代理后的效果。希望通過這篇文章,您能對 CGLIB 動態代理有一個更深入的理解。
本文鏈接:http://www.tebozhan.com/showinfo-26-112781-0.html如何使用 CGLIB 在 Spring Boot 3.3 中實現動態代理
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com