LoginSignup
25
25

More than 5 years have passed since last update.

[Spring boot初心者向け]Aopの使い方

Last updated at Posted at 2018-07-30

この記事の目的

springbootを使って半年足らずの僕が目から鱗だったことを書いていきます。
初心者の方の参考になればと思って書きました。
(springbootを使ってる人には当たり前のことばかりなので参考にならないと思います)

はじめに

Spring BootでWebのBEを開発している際に、ログを出力したいことがありました。その時にAOPで共通処理としてログを埋め込もうと思い、AOPについて調べて実装しました。その実装方法について書きたいと思います。

概要

AOPはアスペクト志向プログラミング(Aspect Oriented Programming)の略で複数のクラスに点在する横断的な関心事(ログとかキャッシュとか)を中心に設計や実装を行うプログラミング手法のことです。
具体的には、@Aspectアノテーションを付与したクラスに共通処理を実装するというもの。もう少し詳しく言いますと、ジョインポイントと呼ばれる部分で共通処理を埋め込む箇所を指定し、該当部分(クラスやメソッドなど)が呼ばれたタイミングで共通処理を実行させるというものです。

では、実際にコードを見ていく前に、AOPを使う上で頻出の用語について軽くまとめておきます。

単語 説明
Aspect AOPの単位となる横断的な関心事を示すモジュールそのもの。「ログの出力」や「例外ハンドリング」などの関心事がAspectになる。
Join point 横断的な関心事を実行するポイントのこと。SpringのAOPではメソッドの実行時。
Advice Join Pointで実行されるコードのこと。
Pointcut 実行対象のJoin Pointを選択する表現のこと。Spring AOPではBean定義ファイルやアノテーションを使って定義する。
Weaving アプリケーションコードの適切なポイントにAspectを入れ込む処理のこと。Spring AOPでは実行時に行う。
Target AOP処理によって処理フローが変更されたオブジェクトのこと。

AOPの実装方法

ここから先では実際に使用する方法をまとめていきます。
まず、依存性を注入します。私の場合はgradleを使用しているので以下の通りです。

build.gradle
compile('org.springframework.boot:spring-boot-starter-aop')

次に、Aspectの実装をしていきます。

SampleAspect.java
package com.sample.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class SampleAspect {
}

ここまで準備ができたら、あとはAdviceを実装していきます。
Spring AOPで実装可能なAdviceは以下の5種類です。

Advice 説明
Before JoinPointの前に実行される。例外のスローをのぞいて、Join Pointの処理フローを防ぐことはできない。
After JoinPointの後に実行される。Join Pointの正常終了や例外のスローにかかわらず、常に実行される。 
AfterReturning JoinPointが正常に終了された場合に実行される。例外がスローされた場合には、実行されない
AfterThrowing JoinPointで例外がスローされた場合に実行される。正常終了した場合には、実行されない
Around JoinPointの前後で実行される。

Beforeを使ったサンプル

SampleAspect.java
package com.sample.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class SampleAspect {
    @Before("execution(* *..*SampleClass.*(..))")
    public void startLog(Joinpoint joinpoint){
        System.out.println("メソッド開始: " + joinpoint.getSignature());
    }
}

Afterを使ったサンプル

SampleAspect.java
package com.sample.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class SampleAspect {
    @After("execution(* *..*SampleClass.*(..))")
    public void startLog(Joinpoint joinpoint){
        System.out.println("メソッド終了: " + joinpoint.getSignature()); 
    }
}

AfterReturningを使ったサンプル

SampleAspect.java
package com.sample.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class SampleAspect {
    @AfterReturning("execution(* *..*SampleClass.*(..))", returning = "sample")
    public void startLog(Joinpoint joinpoint, String Sample){
        System.out.println("メソッド終了: " + joinpoint.getSignature() + "戻り値 = " + sample );
    }
}

AfterThrowingを使ったサンプル

SampleAspect.java
package com.sample.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class SampleAspect {
    @AfterThrowing("execution(* *..*SampleClass.*(..))", throwing = "ex")
    public void startLog(Joinpoint joinpoint, RuntimeException ex){
        System.out.println("メソッド異常終了: " + joinpoint.getSignature());
        ex.printStackTrace;
    }
}

Aroundを使ったサンプル

SampleAspect.java
package com.sample.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aruond;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class SampleAspect {
    @Around("execution(* *..*SampleClass.*(..))")
    public Object log(Joinpoint joinpoint) throws Throwable{
        System.out.println("メソッド開始: " + joinpoint.getSignature());
        try{
            //実行
            Object result = joinpoint.proceed();
            System.out.println("メソッド終了: " + joinpoint.getSignature() + "戻り値 = " + result);
            return result;
        } catch (Exception ex) {
            System.out.println("メソッド異常終了: " + joinpoint.getSignature());
            ex.printStackTrace();
            throw ex;
        }
    }
}

PointCut式について

最後にPointCut式について簡単にまとめておきます。
基本的には以下の通りです。

sample.java
execution(*{パッケージ名}.{クラス}.{メソッド}({引数}))

例えば、

sample.java
execution(*com.sample.user.UserService.*(..)))

ですと、com.sample.user.UserServiceの任意のメソッドを対象にします。

他にも、

sample.java
execution(*com.sample.user.UserService.find*(..)))

ですと、com.sample.user.UserServiceの名前がfindから始まるメソッドを対象にします。
ここには書いてないですが、他にもPointCutの書き方はたくさんあるみたいなので調べてみてください。

参考にしたサイト・書籍

Spring徹底入門

25
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
25