MS_AOP - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki
- AOP: Aspect Oriented Programming
- アスペクト指向プログラミング
- オブジェクト指向ではうまく分離できない問題
各メソッド内に別のメソッドを呼び出す「処理の共通的パターン」がある場合、
オブジェクト指向ではこれを共通化できず、処理が散在してしまう。
を「アスペクト」と呼び、
- アスペクト記述言語(若しくはこれを実現するための各種技術)を用いて、
「アスペクト」を別のメソッド(モジュール)に分離して記述することで、
プログラムに柔軟性を持たせようとする試み。
※ この問題はIoCでも解決できるが、AOP ではコードの外での解決を実現する。
アスペクト記述言語によるウィービングでコードと分離して記述する。
補足(用語): 「オブジェクト指向ではうまく分離できない問題」は、
一般に**横断的関心事(cross-cutting concern)**と呼ばれる。
AOP の用語では次のように整理される。
用語 意味 アスペクト(aspect) 横断的関心事を切り出したモジュール(ロギング等) ジョインポイント(join point) 織り込める場所(メソッド呼び出し、例外送出…) ポイントカット(pointcut) ジョインポイントを選ぶ条件(「 *Serviceの public メソッド」等)アドバイス(advice) 織り込む処理(前 / 後 / 例外時 / 前後を包む) ウィービング(weaving) 実際に織り込む作業
アスペクトの代表的な利用例としては「ロギング処理」がある。
- ロギング処理

- ロギング処理のアスペクト
以下は、AOP を使用してロギング処理の「アスペクト」を
別のモジュール(or 定義)に分離して実装した例である。

実際にコードが実行される際には、ルールに従って指定されたパターンの
「アスペクト」が織り込まれる(これをアスペクトのウィービングと呼ぶ)。
補足(ロギング以外の代表例): 実務で AOP 的な処理が使われるのは、
概ね次の 5 つに集約される。
関心事 .NET での現在の実現手段 ロギング・計測 ミドルウェア、 ILoggerのスコープ、OpenTelemetryトランザクション TransactionScope、MS-DTC、リポジトリ層認可 [Authorize]属性 + フィルター(ASP.NET Core)例外処理・再試行 IExceptionFilter、Polly、HttpClientのハンドラー検証 [Required]等のデータ注釈、FluentValidationつまり 「AOP フレームワークを導入する」形ではなく、
フレームワークが用意した拡張点に載せる形で解決されるのが現在の主流である。
AOP を実現する技術(方式)には下記のものがある。
補足(.NET での実現方式の整理): .NET では次の 4 通りがある。
方式 実現するもの 代表 IL 書き換え(コンパイル後) 静的ウィービング PostSharp / Metalama、Fody 動的プロキシ(実行時) 動的ウィービング Castle DynamicProxy 透過プロキシ( RealProxy)動的ウィービング .NET Framework のみ(後述) ソース ジェネレーター コンパイル時にコード生成 Roslyn(.NETコンパイラ) 重要な変化として、原文が扱う
RealProxy(透過プロキシ)は
.NET Core 以降で利用できない
(System.Runtime.Remotingごと廃止された)。現在の代替は、
DispatchProxy(System.Reflection.DispatchProxy)
… インターフェイスに対する軽量なプロキシ。RealProxyの後継に近い- Castle DynamicProxy
… クラスの virtual メソッドにも織り込める。DI コンテナーとの統合が容易- ソース ジェネレーター
… AOT / トリミングと両立する現在の最有力// DispatchProxy によるインターセプト(.NET Core 以降) public class LoggingProxy<T> : DispatchProxy { private T _inner; protected override object Invoke(MethodInfo m, object[] args) { Console.WriteLine($"→ {m.Name}"); try { return m.Invoke(_inner, args); } finally { Console.WriteLine($"← {m.Name}"); } } public static T Create(T inner) { ... } }ただし、実行時プロキシは Native AOT と
相性が悪い(System.Reflection.Emit に依存するため)。
AOT を視野に入れるならソース ジェネレーターを選ぶ。
代表的な AOP 開発基盤(古いかも)
- AspectJ(Java 用):http://www.eclipse.org/aspectj/
- AspectC++(C/C++ 用):http://www.aspectc.org/
- AspectR(Ruby 用):http://raa.ruby-lang.org/project/aspectr/
- JBossAOP(Java 用):http://www.jboss.org/jbossaop
- Spring Framework(Java 用):http://www.springsource.org/
- Seasar2(Java 用):http://s2container.seasar.org/2.4/ja/
- AspectWerkz(Jonas Boner 等によって開発):http://aspectwerkz.codehaus.org/
補足(.NET 側の選択肢/最新化): 原文が挙げるのは
Java 系が中心で、かつ多くが開発終了している
(AspectWerkz は AspectJ に統合、Seasar2 はサポート終了)。.NET で現在も現役なものを補っておく。
名称 方式 備考 Metalama コンパイル時(Roslyn) PostSharp の後継。商用(無償枠あり) PostSharp IL 書き換え 商用。長い実績 Castle DynamicProxy 動的プロキシ OSS。Moq / DI コンテナーの土台 Fody IL 書き換え OSS。プラグイン形式( PropertyChanged.Fody等)DispatchProxy動的プロキシ 標準ライブラリ。依存を増やさずに済む なお、Java の Spring Framework に相当する Spring .NET は
事実上メンテナンスされておらず、
.NET では標準の DI(.NET Core における DI)を使うのが定石である。
ウィービングの指定方法には、
- 定義ファイル
- アノテーション(メソッド / クラス属性)
- クラスのメソッド構成
などがある。
前述の説明に合わせると、透過プロキシを使用した AOP では、
- アスペクトを透過プロキシ(後述)上に実装する。
- ウィービングされるアスペクトは、使用する透過プロキシにより決定される。
となる。
補足(ウィービングのタイミングによる差): どこで織り込むかによって、
性能・制約・デバッグのしやすさが大きく変わる。① コンパイル時(IL 書き換え / ソース生成) 実行時コスト ほぼゼロ 制約 ビルド手順に組み込む必要 AOT ○(ソース生成なら特に良い) ② 起動時(DI コンテナーがプロキシを生成) 実行時コスト 生成時のみ 制約 インターフェイス or virtual メソッドが必要 AOT △〜× ③ 呼び出し時(都度リフレクション) 実行時コスト 高い 制約 少ない AOT ×② の「インターフェイス or virtual が必要」という制約は実務で効く。
動的プロキシは継承で差し込むため、
sealedクラスや非 virtual メソッドには織り込めない。
- アスペクト指向プログラミング - Wikipedia
https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%B9%E3%83%9A%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0
- Qiita
- AOPが不要だと考える理由
https://qiita.com/yamaokunousausa/items/0e99aef0a0b3adb32d08 - .NET で独自の AOP を導入したら不便になってしまった話
https://qiita.com/KoKeCross/items/4b16ff8cdad65e0fea23
- AOPが不要だと考える理由
補足(なぜ不評なのか): 原文の率直な感想は、
実務での評価とおおむね一致している。理由を整理しておく。
問題 内容 コードを読んでも分からない 属性 1 つの裏で何が起きるか、定義を追わないと不明 デバッグしにくい スタック トレースにプロキシが挟まる/IL が書き換わっている ポイントカットが壊れやすい メソッド名の変更で、静かに織り込まれなくなる 性能が読めない 動的プロキシ・リフレクションのコストが見えない AOT / トリミングと相性が悪い 実行時コード生成に依存する 代替が充実した ミドルウェア、フィルター、 ILogger、OpenTelemetry特に最後の点が大きい。
かつて AOP でしか解けなかった横断的関心事の多くは、
ASP.NET Core のミドルウェア/フィルターや、
HttpClientのデリゲート ハンドラーといった
**「フレームワークが用意した、見える拡張点」**で解決できるようになった。// ミドルウェア:パイプラインに「見える形で」並ぶ app.UseExceptionHandler(); app.UseAuthentication(); app.UseAuthorization(); app.Use(async (ctx, next) => { /* 計測 */ await next(); });同じ「横断的関心事の分離」を、暗黙のウィービングではなく
明示的な合成で行う——これが現在の .NET における回答である。
IoC がフレームワーク側の仕組みとして定着した結果、
AOP 専用基盤を持ち込む必要が薄れた、と読むこともできる。
Tags: 移行, プログラミング, .NET開発