この投稿では、Spring のアスペクト指向プログラミング (AOP) の内部メカニズムをわかりやすく説明します。多くの場合「魔法」の一種と考えられるロギングなどの機能を AOP がどのように実現するかを理解することに焦点を当てます。コア Java 実装を実際に見てみると、本当に魔法のようなものではなく、Java のリフレクション、プロキシ パターン、アノテーションがすべてであることがわかります。
前提条件
- Java コア プロキシ API
- リフレクション API
- アノテーション API
これらはすべて、java.lang.reflect、java.lang.annotation、および javassist.util.proxy パッケージの一部です。
コアメカニズム
Spring AOP の中心には、プロキシ オブジェクト、メソッド インターセプタ、およびリフレクションの概念があります。このパターンの中心となるのは、MethodHandler (または呼び出しハンドラー) です。このハンドラーは、メソッド呼び出しをインターセプトすることによって、プロキシ オブジェクトの動作を制御します。メソッドがプロキシ上で呼び出されると、そのメソッドはハンドラーを介して渡され、リフレクションを介してアノテーションをイントロスペクトできます。適用されたアノテーションに基づいて、必要なロジック (ログなど) を例外の前、後、または例外時に実行できます。
分解する
- プロキシ オブジェクト: これらは、実際のビジネス オブジェクトの代わりに動的に作成されるオブジェクトであり、メソッド ハンドラーを通じてメソッド呼び出しをルーティングします。
- 呼び出しハンドラー: ここでインターセプトの魔法が起こります。リフレクションを使用すると、ハンドラーはターゲット メソッドに存在するアノテーションを調べ、それに応じて動作を変更できます。
- カスタム アノテーション: ロギング、セキュリティ チェック、トランザクション管理などの追加機能をトリガーするマーカーとして機能するカスタム アノテーションを定義できます。
例: 特定のメソッドの実行の前後にログを追加するとします。あらゆる場所でログをハードコーディングする代わりに、メソッドに @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 <p><strong>Main.java テスト クラス</strong><br> </p> <pre class="brush:php;toolbar:false">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 <t> 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> T createProxy(Class<t> 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; } } </t></t></t>
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 中国語 Web サイトの他の関連記事を参照してください。

この記事では、Javaプロジェクト管理、自動化の構築、依存関係の解像度にMavenとGradleを使用して、アプローチと最適化戦略を比較して説明します。

この記事では、MavenやGradleなどのツールを使用して、適切なバージョン化と依存関係管理を使用して、カスタムJavaライブラリ(JARファイル)の作成と使用について説明します。

この記事では、カフェインとグアバキャッシュを使用してJavaでマルチレベルキャッシュを実装してアプリケーションのパフォーマンスを向上させています。セットアップ、統合、パフォーマンスの利点をカバーし、構成と立ち退きポリシー管理Best Pra

この記事では、キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPAを使用することについて説明します。潜在的な落とし穴を強調しながら、パフォーマンスを最適化するためのセットアップ、エンティティマッピング、およびベストプラクティスをカバーしています。[159文字]

Javaのクラスロードには、ブートストラップ、拡張機能、およびアプリケーションクラスローダーを備えた階層システムを使用して、クラスの読み込み、リンク、および初期化が含まれます。親の委任モデルは、コアクラスが最初にロードされ、カスタムクラスのLOAに影響を与えることを保証します


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

DVWA
Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。
