[Spring]観点に向けたプログラミング(AOP)


この記事は、白奇仙のInfolearn講座スプリング入門例(リビジョン)をまとめたものです.
Aspect Oriented Programming
1つのメソッドを実行する場合、一緒に実行する必要があるメソッドがいくつか存在する場合があります.たとえば、認証、ログ・レコード、トランザクション、キャッシュなどの追加機能は、ビジネス・ロジック全体で頻繁に使用されます.これらのコードをコアビジネスロジックとともに記述して使用すると、不要な重複データが発生し、メンテナンス上のトラブルが発生します.一つの分野でこれらの問題を処理することで優雅に問題を解決し、従来の方法を自分の役割に集中させることがAOPの核心である.Springはエージェントモードを用いてAOPを実現する.
 
AOPの実装方法
コンパイル時
これは、*.javaファイルをコンパイルして*.classファイルを生成するときにAOP機能を自動的に追加することを意味する.代表的なのはAspectJです.
バイトコードオペレーション
バイトコード動作方式は、*.classクラスローダがクラスファイルを読み出してメモリにロードする前にAOP関連機能を追加する方法である.
エージェント・モード(Spring AOP)
エージェント・モードは、데코레이터または프록시オブジェクトを使用して、方法の呼び出し部分を迂回することによってAOP機能を提供する一般的な設計モードである.次の例は、実際のスプリングで実装されるAOPコードとは異なる、最も単純な形式のプロキシモードを適用する例を実装する.
Spring 101では、スプリングAOPの直接実施の詳細については詳しくは説明されていない.
// Worker.java
public interface Worker {
    void work();
}

public class HardWorker implements Worker {
    public void work() {
        System.out.println("Work hard!");
    }
}

public class LazyWorker implements Worker {
    public void work() {
        System.out.println("I'm lazy");
    }
}

// WorkerTimer는 Worker 인터페이스를 구현한 클래스의 프록시 클래스러, 핵심 메서드를 실행과 더불어 부가적인 기능(ex, 캐싱, 로깅)을 함께 수행한다.
public class WorkerTimer implements Worker {
    Worker worker;

    public WorkerTimer(Worker worker) {
        this.worker = worker;
    }

    public void work() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        this.worker.work()

        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }
}

public class WorkSpace {
    public void work(Worker worker) {
        worker.work();
    }
}
AOP in Spring
Springが提供するAOP関連機能は非常に豊富です.この記事では,最も簡単な形で記録を担当するAOPを実現した.Springでは、AOPは、AOP를 적용할 Annotation 정의 (Optional)->Aspect 정의->적용범위(Around) 설정のプロセスを実質的に経験する.
// LogExecutionTime.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime { 
}
// LogAspect.java

@Component
@Aspect
public class LogAspect {
    Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTIme(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Object result = joinPoint.proceed();

        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());

        return result;
    }
}
// OwnerController.java

@Controller
public class OwnerController {
    ...

    @GetMapping("/owners/find")
    @LogExecutionTime // Logging AOP에 대한 annotation
    public String processCreationForm(@Valid Owner owner, BindingResult result) {
        ...
    }
}