hyungmuk's blog

스프링 입문강좌 정리 - AOP 애노테이션 본문

개발 & 공부/스프링

스프링 입문강좌 정리 - AOP 애노테이션

hyungmuk 2020. 3. 27. 22:41

5. AOP 애노테이션

애노테이션을 이용한 AOP를 구현하는 예제이다.

만든 것

@LogExecutionTime 애노테이션

메서드에 @LogExecutionTime 가 붙어있으면 해당 메서드의 기능을 측정

소스코드

OwnerController.java

    @GetMapping("/owners/find")
    @LogExecutionTime
    public String initFindForm(Map<String, Object> model) {
        model.put("owner", new Owner());
        return "owners/findOwners";
    }

먼저 컨트롤러에 있는 메서드에 @LogExecutionTime를 붙였다.

 

@LogExecutionTime에 커서를 두고 인텔리제이 에서 Alt + Enter 를 사용하여 LogExecutionTime.java를 생성한다.

 

LogExecutionTime.java

package org.springframework.samples.petclinic.owner;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) // 적용 대상을 메서드로 한다.
@Retention(RetentionPolicy.RUNTIME) // 애노테이션 정보를 런타임까지 유지
public @interface LogExecutionTime {
}

@Target은 만든 애노테이션을 어디에 적용시키는지를 설정하는 것이다. @Target의 인자를 ElementType.METHOD로 설정해서 적용 대상을 메서드로 설정한다.

 

@Retention 은 만든 애노테이션 정보를 언제까지 유지할 것인가를 설정하는 것이다. RetentionPolicy.RUNTIME으로 설정한다.

 

애노테이션을 만들었으면 이제 애노테이션이 붙은 메서드에 대해 AOP를 구현해야 한다.

LogAspect.java를 생성한다.

 

LogAspect.java

package org.springframework.samples.petclinic.owner;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
@Aspect
public class LogAspect {

    Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Around("@annotation(LogExecutionTime)") // LogExecutionTime 애노테이션 주변에 코드를 적용함
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Object proceed = joinPoint.proceed(); // joinPoint 는 LogExecutionTime 애노테이션이 붙어있는 메서드이다. '.proceed()'로 메서드를 실행한다.

        stopWatch.stop();
        logger.info(stopWatch.prettyPrint());

        return proceed; // LogExecutionTime 애너테이션이 붙어있는 메서드를 실행한 결과를 리턴한다.
    }

}

joinPoint 은 @LogExecutionTime 이 붙은 메서드이다.

 

joinPoint.proceed(); 해서 메서드를 실행시킬 수 있다.

메서드를 실행한 결과는 proceed 변수에 담아서 리턴한다.

 

joinPoint.proceed(); 앞뒤로 메서드의 성능을 측정할 수 있도록 코드 (Cross Cutting Concerns) 를 추가한다.

 

핵심적인 기능 말고, 중간중간 삽입되어야 될 기능들, 관심이 횡단관심 (Cross Cutting Concerns) 이다. (로깅, 보안, 트랜잭션, 예외처리)

이에 대비해서 해당 시스템의 핵심 가치와 목적이 그대로 드러난 관심 영역을 핵심 관심 (core concerns) 이라고 부른다.

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Object proceed = joinPoint.proceed(); // joinPoint 는 LogExecutionTime 애노테이션이 붙어있는 메서드이다. '.proceed()'로 메서드를 실행한다.

        stopWatch.stop();
        logger.info(stopWatch.prettyPrint());

실행 결과

@LogExecutionTime가 붙어있는 initFindForm 메서드의 매핑 주소 /owners/find로 접속하니 정상적으로 출력됐다.

2020-03-27 21:58:52.630  INFO 56812 --- [nio-8084-exec-5] o.s.samples.petclinic.owner.LogAspect    : StopWatch '': running time = 83800 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
000083800  100%

 

https://www.youtube.com/watch?v=3750wh1wNuY