AOPに精通

プログラミングパラダイム


IT開発の現代世界では、プログラムを作成するための非常に多くの異なるアプローチがあります。 そのため、たとえば、誰かがプログラムを一連のアクションの形式で提示するのを好む一方で、誰かがプログラムは互いに通信するオブジェクトのセットであると考えています。 これらのアイデアと概念の組み合わせは、プログラムの一種の執筆スタイルを形成します 。これは一般にプログラミングパラダイムと呼ばれます

各パラダイムには独自の特性がありますが、それらを区別する主な要因は、プログラムの基本単位の概念です。 最も人気のあるものは次のとおりです。

一般的な場合、プログラミング言語は使用されるパラダイムを一意に決定しないことに注意してください。同じPHPで、命令型プログラムとオブジェクト指向プログラムの両方を作成できます。

この記事では、比較的若いが、非常に有用なプログラミングパラダイムであるアスペクト指向プログラミングについて説明します。



AOPの基本


次のメソッドを実装する真空内の球状のサービス(Webサービスなど)を検討してください。
public BookDTO getBook(Integer bookId) {
BookDTO book = bookDAO.readBook(bookId);
return book;
}

この方法は非常にシンプルで明白です。本の情報を本の識別子で読むことです。 しかし、ここで何が欠けているのか考えてみましょう。 まず、ロギングについて考える必要があります。ロギングがないと、ご存じのとおり、Webサービスではどこにも行けません。
public BookDTO getBook(Integer bookId) {
LOG.debug( "Call method getBook with id " + bookId);

BookDTO book = bookDAO.readBook(bookId);

LOG.debug( "Book info is: " + book.toString());
return book;
}

次に、例外処理を実装する必要があります(サービス層に対応する例外を返させ、基礎となる層の例外を非表示にするため)。
public BookDTO getBook(Integer bookId) throws ServiceException {
LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = null ;

try {
book = bookDAO.readBook(bookId);
} catch(SQLException e) {
throw new ServiceException(e);
}


LOG.debug( "Book info is: " + book.toString());
return book;
}

また、アクセス権の確認も忘れないでください。
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
if (!SecurityContext.getUser().hasRight("GetBook"))
throw new AuthException("Permission Denied");


LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = null ;

try {
book = bookDAO.readBook(bookId);
} catch (SQLException e) {
throw new ServiceException(e);
}

LOG.debug( "Book info is: " + book.toString());
return book;
}

さらに、作業結果をキャッシュすることは理にかなっています。
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
if (!SecurityContext.getUser().hasRight( "GetBook" ))
throw new AuthException( "Permission Denied" );

LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = null ;
String cacheKey = "getBook:" + bookId;

try {
if (cache.contains(cacheKey)) {
book = (BookDTO) cache.get(cacheKey);
} else {

book = bookDAO.readBook(bookId);
cache.put(cacheKey, book);
}

} catch (SQLException e) {
throw new ServiceException(e);
}

LOG.debug( "Book info is: " + book.toString());
return book;
}

この方法は引き続き改善できますが、最初は十分です。 改善の過程で、元のサイズを超えるメソッドを10回(2〜20 LOC)受け取りました。 最も興味深いのは、その中のビジネスロジックのボリュームが変更されていないことです-それはまだ同じ1行です。 残りのコードは、アプリケーションのいくつかの一般的なユーティリティ機能を実装します:ロギング、エラー処理、アクセス制御、キャッシュなど。

原則として、ビジネスロジックをサービス機能と組み合わせることは、アプリケーションが小さい限りそれほど怖いものではありません。 ただし、プログラムが複雑になるほど、一般的にそのアーキテクチャに注意を払い、特に全体的な機能を強調する必要があります。 そのため、プログラミング言語の進化を観察すると、まず関数、次にモジュール、そしてオブジェクトの外観が見られます。 ただし、いくつかの一般的な機能を強調するには、上記のパラダイムでは不十分であることが実践で示されています。 そのような機能は「パススルー」または「分散」と呼ばれます。これは、その実装が実際にアプリケーションのさまざまな部分に分散しているためです。 上記のクロスカット機能の例は次のとおりです。

アスペクト指向プログラミング(AOP)の主な目的は、エンドツーエンド機能のモジュール化、アスペクトへの割り当てです。 これを行うために、AOPの概念をサポートする言語は、次のツールを実装して、エンドツーエンドの機能を強調しています。


使用例(AspectJの)


AspectJは、Java言語のアスペクト指向の拡張機能/フレームワークです。 現時点では、これはおそらく最も人気があり開発中のAOPエンジンです。

その助けを借りて、ロギングの側面の実装を検討してください。
@Aspect
public class WebServiceLogger {
private final static Logger LOG =
Logger.getLogger(WebServiceLogger. class );

@Pointcut( "execution(* example.WebService.*(..))" )
public void webServiceMethod() { }

@Pointcut( "@annotation(example.Loggable)" )
public void loggableMethod() { }

@Around( "webServiceMethod() && loggableMethod()" )
public Object logWebServiceCall(ProceedingJoinPoint thisJoinPoint) {
String methodName = thisJoinPoint.getSignature().getName();
Object[] methodArgs = thisJoinPoint.getArgs();

LOG.debug( "Call method " + methodName + " with args " + methodArgs);

Object result = thisJoinPoint.proceed();

LOG.debug( "Method " + methodName + " returns " + result);

return result;
}
}

最初のステップは、サービスメソッドのロギングのアスペクト 、つまりAspectアノテーションでマークされたWebServiceLoggerクラスを作成することです。 次に、接合点の2つのスライスが定義されます:webServiceMethod(WebServiceクラスに属するメソッドを呼び出す)とloggableMethod(@Loggableアノテーションでマークされたメソッドを呼び出す)。 最後に、ヒントが宣言され(logWebServiceCallメソッド)、スライスを満たす接続ポイント( Aroundアノテーション)の代わりに実行されます( "webServiceMethod()&& loggableMethod()")。

評議会コードでは、現在のメソッド(接続ポイント)、メソッドの開始のログ、要求されたメソッドの直接呼び出し、作業の結果のログと返送に関する情報を受け取ります。

AspectJには、サポートされている接続ポイントのスライスがかなり大量にあります。 主なものは次のとおりです。

ヒントについては、その数ははるかに少ないですが、必要な多くの状況をすべて網羅しています。

AspectJデザインの詳細については、 公式ドキュメントの対応するセクション[ 1、2 ]を参照してください。

AspectJアスペクトを使用するには、特別なAJCコンパイラを使用してコンパイルし、メインクラスに縫い付ける必要があります。

製品は無料です。 Eclipseライセンスの下で配布されます。



使用例(PostSharp)


PostSharpは、.NETプラットフォーム用のアスペクト指向フレームワークです。 .NETには他にもAOPの実装がありますが、PostSharpサイトからの比較から判断する 、主導的地位を保持しているのは彼です。

例外処理の側面を説明するための使用方法を検討してください。 最初のステップは、関連するアスペクトを拡張するクラスを作成することです:

public class ExceptionDialogAttribute : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
string message = eventArgs.Exception.Message;
Window window = Window.GetWindow((DependencyObject)eventArgs.Instance);
MessageBox.Show(window, message, "Exception" );
eventArgs.FlowBehavior = FlowBehavior.Continue;
}
}


厳密に言えば、PostSharpの用語の側面は、見てわかるように、AOPの用語の側面とアドバイスです。

このアスペクトの交差点のスライスを指定するには、アセンブリ設定ファイル(AssemblyInfo.cs)に次の行を追加します。

[assembly: ExceptionDialog ( AttributeTargetTypes= "Example.WorkflowService.*" ,
AttributeTargetMemberAttributes = AttributeTargetElements.Public )]


または、ExceptionDialog属性を使用して、目的のメソッドを明示的にマークします。

[ExceptionDialog]
public BookDTO GetBook(Integer bookId)


それだけです。対応するメソッドでスローされるすべての例外は、作成されたアスペクトによって処理されます。

PostSharpがアドバイスとアスペクトの概念を部分的に接着しているという事実のため、後者にはかなりの数があります。 詳細はドキュメントに記載されています 。 主なものは次のとおりです。

PostSharpを機能させるには、プロジェクトに接続する必要のあるコンパイラとライブラリが必要です。 ステッチングの側面は、アプリケーションのビルド中に後処理バイトコードに基づいています。

製品は支払われます。 コミュニティ版があります。



理論から実践へ


それで、アプリケーションのエンドツーエンド機能の「括弧から抜け出す」という問題をどれほど美しく効果的に解決できるかを見てきました。 ただし、これはすべて理論です。 実際には、もちろん、すべてが少し異なります:)

まず、どちらの場合も、アスペクトのコンパイルと「ウィービング」のために、特別なコンパイラを使用し、プロジェクトとともに追加のライブラリをドラッグする必要があります。 これは問題ではないようです:コンパイラは簡単にダウンロードして環境に統合できます(たとえば、mavenを使用する場合、タスクはアスペクトj-maven-pluginプラグインの追加に削減されます)、少なくともJavaアプリケーションでは多くの依存関係が一般的です(同じMavenを使用して解決)。 ただし、プロジェクトに個別のコンパイルが必要なものを含める必要があり、潜在的な利点にもかかわらずすべての開発者を怖がらせることがあります。

この場合、Spring Framework [ 1、2 ]が問題の解決策になる可能性があります。 このフレームワークには多くの利点がありますが、この記事のフレームワーク内では、AOPコンポーネントに関心があります。 Spring Frameworkは、プロキシオブジェクト(JDK Dynamic Proxy、CGLIB)を作成することにより、サードパーティライブラリを使用せずに、Pure Java(C#)で制限されたAOP機能を実装します。 つまり、Spring AOPでは、「メソッド実行」タイプの結合ポイントのみを使用できます。 ただし、実践が示すように、ほとんどの問題を解決するにはこのタイプの接続ポイントが必要なので、この制限は重要な役割を果たしません。

さらに、Spring Frameworkは、@ AspectJアノテーションを使用したアプリケーションの構成、およびAspectJを使用して直接コンパイルされたアスペクトの統合をサポートします。

当社では、Spring AOPを使用しています。 私の意見では、Spring Frameworkのその他のメリットを考慮すると、AOPを使用するのに最もアクセスしやすく便利なプラットフォームであり、その普及と開発に大きく貢献しています。



まとめ


要約すると、AOPに関する3つの主な考えを強調したいと思います。


関連リンク


Source: https://habr.com/ru/post/J114649/


All Articles