목차
- 예제 만들기
- 로그 출력 AOP
- 재시도 AOP
예제 만들기
- @Trace 애노테이션으로 로그 출력하기
- @Retry 애노테이션으로 예외 발생시 재시도 하기
먼저 AOP를 적용할 예제를 만들자.
ExamRepository
@Repository
public class ExamRepository {
private static int seq = 0;
/**
* 5번에 1번 실패하는 요청
*/
public String save(String itemId) {
seq++;
if (seq % 5 == 0) {
throw new IllegalStateException("예외 발생");
}
return "ok";
}
}
- 5번에 1번 정도 실패하는 저장소이다. 이렇게 간헐적으로 실패할 경우 재시도 하는 AOP가 있으면 편리하다.
ExamService
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
public void request(String itemId) {
examRepository.save(itemId);
}
}
ExamTest
@SpringBootTest
public class ExamTest {
@Autowired
ExamService examService;
@Test
void test() {
for (int i = 0; i < 5; i++) {
examService.request("data" + i);
}
}
}
- 실행해보면 테스트가 5번째 루프를 실행할 때 리포지토리 위치에서 예외가 발생하면서 실패하는 것을 확인할 수 있다.
로그 출력 AOP
먼저 로그 출력용 AOP를 만들어보자.
@Trace 가 메서드에 붙어 있으면 호출 정보가 출력되는 편리한 기능이다.
@Trace
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trace {
}
TraceAspect
@Aspect
@Slf4j
public class TraceAspect {
@Before("@annotation(hello.aop.exam.annotation.Trace)")
public void before(JoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
log.info("[trace] {} args={}", joinPoint.getSignature(), args);
}
}
- @annotation(hello.aop.exam.annotation.Trace) 포인트컷을 사용해서 @Trace 가 붙은 메서드에 어드바이스를 적용한다.
ExamService - @Trace 추가
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
@Trace
public void request(String itemId) {
examRepository.save(itemId);
}
}
- request() 에 @Trace 를 붙였다.
- 이제 메서드 호출 정보를 AOP를 사용해서 로그로 남길 수 있다.
ExamRepository - @Trace 추가
@Repository
public class ExamRepository {
private static int seq = 0;
/**
* 5번에 1번 실패하는 요청
*/
@Trace
public String save(String itemId) {
// ..
}
}
- save() 에 @Trace 를 붙였다.
ExamTest - 추가
@SpringBootTest
@Import({TraceAspect.class})
public class ExamTest {
}
- @Import(TraceAspect.class) 를 사용해서 TraceAspect 를 스프링 빈으로 추가하자. 이제 애스펙트가 적용된다.
실행 결과
[trace] void hello.aop.exam.ExamService.request(String) args=[data0]
[trace] String hello.aop.exam.ExamRepository.save(String) args=[data0]
[trace] void hello.aop.exam.ExamService.request(String) args=[data1]
[trace] String hello.aop.exam.ExamRepository.save(String) args=[data1]
...
- 실행해보면 @Trace 가 붙은 request() , save() 호출시 로그가 잘 남는 것을 확인할 수 있다.
재시도 AOP
이번에는 좀 더 의미있는 재시도 AOP를 만들어보자.
@Retry 애노테이션이 있으면 예외가 발생했을 때 다시 시도해서 문제를 복구한다.
@Retry
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retry {
int value() default 3;
}
- 이 애노테이션에는 재시도 횟수로 사용할 값이 있다. 기본값으로 3 을 사용한다
RetryAspect
@Aspect
@Slf4j
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;
}
}
- 재시도 하는 애스펙트이다.
- @annotation(retry) , Retry retry 를 사용해서 어드바이스에 애노테이션을 파라미터로 전달한다.
- retry.value() 를 통해서 애노테이션에 지정한 값을 가져올 수 있다.
- 예외가 발생해서 결과가 정상 반환되지 않으면 retry.value() 만큼 재시도한다.
ExamRepository - @Retry 추가
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
@Trace
@Retry(value = 4)
public void request(String itemId) {
examRepository.save(itemId);
}
}
- ExamRepository.save() 메서드에 @Retry(value = 4) 를 적용했다.
- 이 메서드에서 문제가 발생하면 4번 재시도 한다.
ExamTest - 추가
@SpringBootTest
//@Import(TraceAspect.class)
@Import({TraceAspect.class, RetryAspect.class})
public class ExamTest {
}
실행 결과
...
[retry] try count=1/5
[retry] try count=2/5
- 실행 결과를 보면 5번째 문제가 발생했을 때 재시도 덕분에 문제가 복구되고, 정상 응답되는 것을 확인할 수 있다.
참고
- 스프링이 제공하는 @Transactional 은 가장 대표적인 AOP이다.
'Spring' 카테고리의 다른 글
[Spring] @Profile (0) | 2024.11.04 |
---|---|
[Spring] 스프링 AOP - 실무 주의사항 (1) | 2024.09.25 |
[Spring] 스프링 AOP - 포인트컷 (0) | 2024.09.24 |
[Spring] 스프링 AOP 구현 (0) | 2024.09.23 |
[Spring] 스프링 AOP 개념 (1) | 2024.09.20 |