인프런 김영한님의 '스프링 핵심 원리-고급편' 강의 보러가기
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8/dashboard
지금까지 학습한 내용을 활용해서 유용한 스프링 AOP를 만들어보자.
@Trace 애노테이션으로 로그 출력하기
@Retry 애노테이션으로 예외 발생시 재시도 하기
package hello.aop.exam;
import hello.aop.exam.annotation.Retry;
import hello.aop.exam.annotation.Trace;
import org.springframework.stereotype.Repository;
@Repository
public class ExamRepository {
private static int seq = 0;
@Trace
public String save(String itemId) {
seq++;
if (seq % 5 == 0) {
throw new IllegalArgumentException("예외 발생");
}
return "ok";
}
}
package hello.aop.exam;
import hello.aop.exam.annotation.Trace;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
public void request(String itemId) {
examRepository.save(itemId);
}
}
package hello.aop.exam;
import hello.aop.exam.annotation.Retry;
import hello.aop.exam.aop.RetryAspect;
import hello.aop.exam.aop.TraceAspect;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Slf4j
public class ExamTest {
@Autowired
ExamService examService;
@Test
void test() {
for (int i = 0; i < 5; i++) {
log.info("client request = {}", i);
examService.request("data" + i);
}
}
}
실행해보면 테스트가 5번째 루프를 실행할 때 리포지토리 위치에서 예외가 발생하면서 실패하는 것을 확인할 수 있다.
package hello.aop.exam.annotation;
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 Trace {
}
package hello.aop.exam.aop;
import hello.aop.exam.annotation.Trace;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Slf4j
@Aspect
public class TraceAspect {
@Before("@annotation(trace)")
public void doTrace(JoinPoint joinPoint, Trace trace) {
Object[] args = joinPoint.getArgs();
log.info("[trace] {} args={}", joinPoint.getSignature(), args);
}
}
package hello.aop.exam.annotation;
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 Retry {
int value() default 3;
}
package hello.aop.exam.aop;
import hello.aop.exam.annotation.Retry;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Slf4j
@Aspect
public class RetryAspect {
@Around("@annotation(retry)")
public Object doRetry(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
log.info("[retry] {} retry={}", joinPoint.getSignature(), retry);
int maxRetry = retry.value();
Exception exceptionHolder = null;
for (int retryCount = 1; retryCount <= maxRetry; retryCount++) {
try {
log.info("[retry] try count={}/{}", retryCount, maxRetry);
return joinPoint.proceed();
} catch (Exception e) {
exceptionHolder = e;
}
}
throw exceptionHolder;
}
}
package hello.aop.exam;
import hello.aop.exam.annotation.Trace;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
@Trace
public void request(String itemId) {
examRepository.save(itemId);
}
}
request() 에 @Trace 를 붙였다. 이제 메서드 호출 정보를 AOP를 사용해서 로그로 남길 수 있다.
package hello.aop.exam;
import hello.aop.exam.annotation.Retry;
import hello.aop.exam.annotation.Trace;
import org.springframework.stereotype.Repository;
@Repository
public class ExamRepository {
private static int seq = 0;
@Trace
@Retry(4)
public String save(String itemId) {
seq++;
if (seq % 5 == 0) {
throw new IllegalArgumentException("예외 발생");
}
return "ok";
}
}
ExamRepository.save() 메서드에 @Retry(value = 4) 를 적용했다. 이 메서드에서 문제가 발생하면 4번 재시도 한다
ExamTest - 추가
@SpringBootTest
//@Import(TraceAspect.class)
@Import({TraceAspect.class, RetryAspect.class})
public class ExamTest {
}