首頁> Java> java教程> 主體

揭示 Spring AOP 的內部運作原理

王林
發布: 2024-09-07 06:34:36
原創
489 人瀏覽過

Unveiling the Inner Workings of Spring AOP

在這篇文章中,我們將揭秘 Spring 中面向面向程式設計(AOP)的內部機制。重點將放在理解 AOP 如何實現日誌記錄等功能,這些功能通常被認為是一種「魔法」。透過瀏覽核心 Java 實現,我們將了解 Java 的反射、代理模式和註釋,而不是任何真正神奇的東西。

先決條件

  • Java 核心代理程式 API
  • 反射API
  • 註解API

這些都是 java.lang.reflect、java.lang.annotation 和 javassist.util.proxy 套件的一部分。

核心機制

Spring AOP 的核心是代理物件、方法攔截器和反射的概念。此模式中的關鍵角色是 MethodHandler(或呼叫處理程序)。此處理程序透過攔截方法呼叫來控制代理物件的行為。當在代理上呼叫方法時,它會透過處理程序傳遞,其中可以透過反射對註釋進行內省。根據所應用的註釋,可以在異常之前、之後或異常時執行必要的邏輯(例如日誌記錄)。

分解它

  1. 代理對象:這些是動態建立的對象,代表您的實際業務對象,透過方法處理程序路由方法呼叫。
  2. 呼叫處理程序:這就是攔截的神奇之處。使用反射,處理程序可以檢查目標方法上存在的註釋並相應地改變行為。
  3. 自訂註釋:您可以定義自訂註釋,它們用作觸發日誌記錄、安全檢查或事務管理等附加功能的標記。

範例:假設我們想要在某些方法執行之前和之後加入日誌記錄。我們可以使用 @BeforeMethod 和 @AfterMethod 註解方法,而不是到處硬編碼日誌記錄。我們的處理程序檢查此註釋的方法並動態添加適當的日誌記錄邏輯。

以下是我們範例中控制器和服務的類別。

WorkerController.java

package edu.pk.poc.aop.controller; import edu.pk.poc.aop.annotation.AfterMethod; import edu.pk.poc.aop.annotation.All; import edu.pk.poc.aop.annotation.BeforeMethod; import edu.pk.poc.aop.helper.ProxyFactory; import edu.pk.poc.aop.service.Worker; import edu.pk.poc.aop.service.WorkerService; import edu.pk.poc.aop.service.WorkerServiceImpl; public class WorkerController { WorkerService workerService = ProxyFactory.createProxy(WorkerServiceImpl.class); /** * This Method 1s annotated with @BeforeMethod and @AfterMethod, So the log statements * will be generated before and after method call. */ @BeforeMethod @AfterMethod public void engageFullTimeWorker() throws Exception { Worker fullTimeWorker = new Worker(); fullTimeWorker.setName("FullTime-Worker"); fullTimeWorker.setPartTime(false); fullTimeWorker.setDuration(9); workerService.doWork(fullTimeWorker); } /** * This Method is annotated with @All, So the log statements will be generated before and after method call * along with exception if raised. */ @All public void engagePartTimeWorker() throws Exception { Worker partTimeWorker = new Worker(); partTimeWorker.setName("PartTime-Worker"); partTimeWorker.setPartTime(true); partTimeWorker.setDuration(4); workerService.doWork(partTimeWorker); } }
登入後複製

WorkerServiceImpl.java

package edu.pk.poc.aop.service; import edu.pk.poc.aop.annotation.AfterMethod; public class WorkerServiceImpl implements WorkerService { /** * Here this method is annotated with only @AfterMethod, So only log statement * will be generated after method call */ @AfterMethod @Override public void doWork(Worker worker) throws Exception { if (worker.isPartTime()) { throw new Exception("Part time workers are not permitted to work."); } System.out.print("A full time worker is working for " + worker.getDuration() + " hours :: "); for (int i = 1; i < worker.getDuration(); i++) { System.out.print("* "); } System.out.println(); } }
登入後複製

Main.java 測試類別

package edu.pk.poc.aop.test; import edu.pk.poc.aop.controller.WorkerController; import edu.pk.poc.aop.helper.ProxyFactory; import edu.pk.util.Logger; public class Main { public static void main(String[] args) { WorkerController controller = ProxyFactory.createProxy(WorkerController.class); Logger logger = new Logger(); try { System.out.println("Testing @BeforeMethod and @AfterMethod"); System.out.println("-----------------------------------------"); controller.engageFullTimeWorker(); System.out.println("Testing @All"); System.out.println("-----------------------------------------"); controller.engagePartTimeWorker(); } catch (Exception e) { logger.error("Exception caught in Main class"); } } }
登入後複製

輸出

Testing @BeforeMethod and @AfterMethod ----------------------------------------- >>> Entering into edu.pk.poc.aop.controller.WorkerController.engageFullTimeWorker() A full time worker is working for 9 hours :: * * * * * * * * >>> Exiting from edu.pk.poc.aop.service.WorkerServiceImpl.doWork() >>> Exiting from edu.pk.poc.aop.controller.WorkerController.engageFullTimeWorker() Testing @All ----------------------------------------- >>> Entering into edu.pk.poc.aop.controller.WorkerController.engagePartTimeWorker() >>> Exception in edu.pk.poc.aop.controller.WorkerController.engagePartTimeWorker() Exception caught in Main class
登入後複製

它是如何運作的

當在代理物件上呼叫方法時,該呼叫會被處理程序攔截,處理程序使用反射來檢查目標方法上的所有註解。根據這些註釋,處理程序決定是否記錄方法進入/退出、記錄異常或完全跳過記錄。

以下是可視化它的方法:

  • 執行前:記錄方法條目。
  • 執行後:記錄方法退出或成功。
  • 全部:記錄方法條目、方法條目以及引發的異常。 這種動態行為表明 Spring AOP 利用了核心 Java API,而不是使用一些神奇的技巧。

定義註解

package edu.pk.poc.aop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AfterMethod { }
登入後複製
package edu.pk.poc.aop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface BeforeMethod { }
登入後複製
package edu.pk.poc.aop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface All { }
登入後複製

定義代理工廠

package edu.pk.poc.aop.helper; /** * The {@code ProxyFactory} class is responsible for creating proxy objects using the Javassist library. * It allows for dynamic generation of proxies for classes or interfaces, with support for method interception. */ public class ProxyFactory { /** * A Javassist ProxyFactory instance used to generate proxy classes. */ private static final javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory(); /** * Creates a proxy object for the given class or interface. * If the class is an interface, the proxy implements the interface. * If it's a concrete class, the proxy extends the class. * * @param  the type of the class or interface for which the proxy is to be created * @param klass the {@code Class} object representing the class or interface to proxy * @return a proxy instance of the specified class or interface, or {@code null} if proxy creation fails */ public static  T createProxy(Class klass) { if (klass.isInterface()) factory.setInterfaces(new Class[]{klass}); else factory.setSuperclass(klass); try { return (T) factory.create(new Class[0], new Object[0], new AOPLoggingMethodHandler()); } catch (Exception e) { System.err.println(e.getMessage()); } return null; } }
登入後複製

定義 MethodHandler

package edu.pk.poc.aop.helper; import edu.pk.poc.aop.annotation.AfterMethod; import edu.pk.poc.aop.annotation.All; import edu.pk.poc.aop.annotation.BeforeMethod; import edu.pk.poc.aop.annotation.OnException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import edu.pk.util.Logger; import javassist.util.proxy.MethodHandler; public class AOPLoggingMethodHandler implements MethodHandler { private static final Logger logger = new Logger(); public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { if (proceed != null) { // Concrete Method Object result = null; String className = resolveClassName(self); try { if (isAnnotationPresent(thisMethod, BeforeMethod.class) || isAnnotationPresent(thisMethod, All.class)) { logger.info(">>> Entering into " + className + "." + thisMethod.getName() + "()"); } result = proceed.invoke(self, args); if (isAnnotationPresent(thisMethod, AfterMethod.class) || isAnnotationPresent(thisMethod, All.class)) { logger.info(">>> Exiting from " + className + "." + thisMethod.getName() + "()"); } } catch (Throwable t) { if (isAnnotationPresent(thisMethod, OnException.class) || isAnnotationPresent(thisMethod, All.class)) { logger.error(">>> Exception in " + className + "." + thisMethod.getName() + "()"); } throw t; } return result; } throw new RuntimeException("Method is Abstract"); } private boolean isAnnotationPresent(Method method, Class klass) { Annotation[] declaredAnnotationsByType = method.getAnnotationsByType(klass); return declaredAnnotationsByType != null && declaredAnnotationsByType.length > 0; } private String resolveClassName(Object self) { String className = self.getClass().getName(); if (className.contains("_$$")) { className = className.substring(0, className.indexOf("_$$")); } return className; } }
登入後複製

結論

Spring AOP 是一個用於橫切關注點的強大工具,但它並沒有做任何革命性的事情。它建立在反射和代理等核心 Java 概念之上,這些概念在語言本身中可用。透過理解這一點,您可以更好地理解 Spring 如何簡化這些底層機制以方便開發人員。

以上是揭示 Spring AOP 的內部運作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!