Spring

[Spring] AOP 관점지향 프로그래밍 | 횡단 관심사 분리

kimslab01 2024. 9. 10. 22:59

 

 

 

 

a. aspect 만들기

@Slf4j
@Aspect
/**
 * 애스팩트: 어드바이스와 포인트컷을 하나로 묶은 모듈
 */
public class AspectPractice {
    // 포인트컷
    // 어드바이스 위치
}

 

 

  • AOP 등록
@Configuration
public class WebConfig {

    @Bean
    public AspectPractice getAspectPracticeAop() {
        return new AspectPractice();
    }

}

 

 

 

 

b. 패키지 범위 기반 포인트컷 만들기

    /**
     * 포인트컷: 서비스 패키지 기반
     */
    @Pointcut("execution(* com.standard.sparta.service..*(..))")
    private void serviceLayer() {}

 

 

 

 

c. 어드바이스 종류 살펴보기

1. Before

    /**
     * 어드바이스: @Before
     * 메서드 실행 전에 수행되는 로직을 처리할때 사용합니다.
     */
    @Before("serviceLayer()")
    public void beforeMethod(){
        log.info("::: BEFORE 실행 :::");
    }

 

 

 

2. AfterReturning

    /**
     * 어드바이스: @AfterReturning
     * 메서드가 정상적으로 반환된 후에 실행됩니다.
     * 예외가 발생하지 않고 정상적으로 결과값을 반환했을때만 동작합니다.
     * @param result 성공시 반환값
     */
   @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void afterReturningMethod(Object result) {
       // result 에 대한 작업을 수행할 수 있겠죠?
       log.info("::: AFTER RETURNING :::");
    }

 

 

 

3.AfterThrowing

    /**
     * 어드바이스: @AfterThrowing
     * 메서드 실행 중 예외가 발생했을 때만 실행됩니다.
     * @param joinPoint 조인포인트
     * @param ex 발생한예외 객체
     */
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
        // 서비스에서 발생한 예외 ex.getMessage() <- 예외 조작 가능
        log.info("::: AFTER THROWING :::");
    }

 

 

 

4. After

    /**
     * 어드바이스: @After
     * 메서드가 정상적으로 실행되던 예외가 발생하던 메서드가 완료된 후에 항상 실행됩니다.
     */
    @After("serviceLayer()")
    public void afterMethod(JoinPoint joinPoint) {
        log.info("::: AFTER :::");
    }

 

 

 

5. Around

    /**
     * 어드바이스: 가장 강력한 어드바이저, 전체 흐름을 제어할 수 있습니다.
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("serviceLayer()")  // 어노테이션 기반으로 포인트컷 설정
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("::: BEFORE :::");
        try {
            Object result = joinPoint.proceed();
            System.out.println(result);
            log.info("::: AFTER RETURNING :::");
            return result;

        } catch (Exception e) {
            log.info("::: AFTER THROWING :::");
            throw e;

        } finally {
            log.info("::: AFTER :::");
        }
    }

 

 

 

 

 

d. 패키지 범위 기반 어드바이스 적용

    /**
     * 어드바이스: 패키지 범위 기반
     */
    @Around("serviceLayer()")
    public Object advicePackage(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        try {
            return joinPoint.proceed();

        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            log.info("::: ExecutionTime: {}ms", executionTime);
        }
    }

 

 

 

 

 

e. 어노테이션 범위 기반 어드바이스 적용

 

1. 어드바이스 생성

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {
}

 

 

 

2. 포인트컷 생성

    /**
     * 포인트컷: 어토네이션 기반
     */
    @Pointcut("@annotation(com.standard.sparta.aop.annotation.TrackTime)")
    private void trackTimeAnnotation() {}

 

 

 

3. 어드바이스

    /**
     * 어드바이스: 어노테이션 범위 기반
     */
    @Around("trackTimeAnnotation()")
    public Object adviceAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
        //측정 시작
        long startTime = System.currentTimeMillis();

        try {
            Object result = joinPoint.proceed();
            return result;
        } finally {
            // 측정 완료
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            log.info("::: ExecutionTime: {}ms", executionTime);
        }
    }