블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.github.io/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
반응형
AOP 구현
AOP 개념은 아래 글 참고
2021.03.11 - [Spring] - Spring의 AOP 개념 (Aspect Oriented Programming)
의존성 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
서비스(비즈니스 로직) 구현
테스트를 위한 비즈니스 로직을 구현
- AOP 구현에 집중하기 위해, 단순히 2개 정수로 사칙연산하는 메소드를 생성
public interface CalcService {
int sum(int x, int y);
int subtract(int x, int y);
int multiply(int x, int y);
int divide(int x, int y);
}
public class CalcServiceImpl implements CalcService{
@Override
public int sum(int x, int y) {
return x + y;
}
@Override
public int subtract(int x, int y) {
return x - y;
}
@Override
public int multiply(int x, int y) {
return x * y;
}
@Override
public int divide(int x, int y) {
return x / y;
}
}
Aspect 구현
로그를 남기는 Aspect를 구현한다.
- @Around 로 앞, 뒤로 실행하는 Advice를 구현
@Aspect
@Component
public class LogAspect { // Aspect : 부가 기능 구현체들을 포함하고 있는 모듈
private final Logger logger = LoggerFactory.getLogger(this.getClass());
// PointCut : 적용할 지점 또는 범위 선택
@Pointcut("execution(public * com.example.demo.service..*(..))")
private void publicTarget() { }
// Advice : 실제 부가기능 구현부
@Around("publicTarget()")
public Object calcPerformanceAdvice(ProceedingJoinPoint pjp) throws Throwable {
logger.info("성능 측정을 시작합니다.");
StopWatch sw = new StopWatch();
sw.start();
// 비즈니스 로직 (메인 로직)
Object result = pjp.proceed();
sw.stop();
logger.info("성능 측정이 끝났습니다.");
logger.info("걸린시간: {} ms", sw.getLastTaskTimeMillis());
return result;
}
}
실행
메소드 앞뒤로 실행 됨
@SpringBootTest
public class AopTest {
@Autowired
private CalcService calcService;
@Test
@DisplayName("AOP - Around 테스트")
void aopAroundTest() {
calcService.sum(3, 5);
}
}
2021-03-11 11:03:36.237 INFO 19720 --- [main] com.example.demo.LogAspect : 성능 측정을 시작합니다.
2021-03-11 11:03:36.246 INFO 19720 --- [main] com.example.demo.LogAspect : 성능 측정이 끝났습니다.
2021-03-11 11:03:36.246 INFO 19720 --- [main] com.example.demo.LogAspect : 걸린시간: 9 ms
Advice는 5가지 종류가 있음
- Before Advice: Target 메소드 호출 전에 적용 (
@Before
사용) - After returning: Target 메소드 호출 후 적용 (
@AfterReturnning
사용) - After throwing: Target에서 예외 발생 후 적용 (
@AfterThrowing
사용) - After: Target 메소드 호출 후 예외 발생에 상관없이 적용 (
@After
사용) - Around: Target 메소드 호출 전/후 적용 (
@Around
사용)
@Before 예시
subtract() 빼기 연산 메소드 전에 로그를 찍어주는 Advice 추가
@Aspect
@Component
public class LogAspect {
// ...
@Pointcut("execution(public * com.example.demo.service.CalcService.subtract(..))")
private void subtractTarget() { }
@Before("subtractTarget()")
public void subtractBeforeAdvice() {
logger.info("빼기 연산을 수행합니다.");
}
}
그리고, 실행
@Test
@DisplayName("AOP - Before 테스트")
void aopBeforeTest() {
calcService.subtract(100, 20);
}
2021-03-11 11:17:51.976 INFO 11668 --- [main] com.example.demo.LogAspect: 성능 측정을 시작합니다.
2021-03-11 11:17:51.976 INFO 11668 --- [main] com.example.demo.LogAspect: 빼기 연산을 수행합니다.
2021-03-11 11:17:52.020 INFO 11668 --- [main] com.example.demo.LogAspect: 성능 측정이 끝났습니다.
2021-03-11 11:17:52.021 INFO 11668 --- [main] com.example.demo.LogAspect: 걸린시간: 44 ms
@After 예시
multiply() 메소드 실행 후에 로그를 찍어주는 Advice 추가
@Aspect
@Component
public class LogAspect {
// ...
@Pointcut("execution(public * com.example.demo.service.CalcService.multiply(..))")
private void multiplyTarget() { }
@After("multiplyTarget()")
public void multiplyAfterAdvice() {
logger.info("곱하기 연산이 끝났습니다");
}
}
그리고, 실행
@Test
@DisplayName("AOP - After 테스트")
void aopAfterTest() {
calcService.multiply(3, 10);
}
2021-03-11 11:21:47.952 INFO 13624 --- [main] com.example.demo.LogAspect: 성능 측정을 시작합니다.
2021-03-11 11:21:47.953 INFO 13624 --- [main] com.example.demo.LogAspect: 곱하기 연산이 끝났습니다
2021-03-11 11:21:47.963 INFO 13624 --- [main] com.example.demo.LogAspect: 성능 측정이 끝났습니다.
2021-03-11 11:21:47.963 INFO 13624 --- [main] com.example.demo.LogAspect: 걸린시간: 10 ms
@AfterRenturnning 예제
메소드 실행하고 return까지 반환한 뒤 실행
@Aspect
@Component
public class LogAspect {
// ...
@Pointcut("execution(public * com.example.demo.service.CalcService.sum(..))")
private void sumTarget() { }
@AfterReturning(value = "sumTarget()", returning = "returnValue")
public void sumAfterReturningAdvice(Object returnValue) {
logger.info("연산 값은 {} 입니다.", returnValue);
}
}
그리고, 실행
@Test
@DisplayName("AOP - AfterReturning 테스트")
void aopAfterReturningTest() {
calcService.sum(10, 2);
}
2021-03-11 11:29:23.073 INFO 20512 --- [main] com.example.demo.LogAspect: 성능 측정을 시작합니다.
2021-03-11 11:29:23.082 INFO 20512 --- [main] com.example.demo.LogAspect: 연산 값은 12 입니다.
2021-03-11 11:29:23.083 INFO 20512 --- [main] com.example.demo.LogAspect: 성능 측정이 끝났습니다.
2021-03-11 11:29:23.083 INFO 20512 --- [main] com.example.demo.LogAspect: 걸린시간: 10 ms
@AfterThrowing 예제
0으로 나누는것이 불가능하기 때문에 Exception 발생, 그때 실행
@Aspect
@Component
public class LogAspect {
// ...
@Pointcut("execution(public * com.example.demo.service.CalcService.divide(..))")
private void divideTarget() { }
@AfterThrowing(value = "divideTarget()", throwing = "exception")
public void sumAfterReturningAdvice(Exception exception) {
logger.info("나누기 연산 도주에 ERROR({}) 발생", exception.getMessage());
}
}
그리고 실행
@Test
@DisplayName("AOP - AfterReturning 테스트")
void aopAfterThrowingTest() {
calcService.divide(10, 0);
}
2021-03-11 11:33:00.408 INFO 18032 --- [main] com.example.demo.LogAspect: 성능 측정을 시작합니다.
2021-03-11 11:33:00.419 INFO 18032 --- [main] com.example.demo.LogAspect: 나누기 연산 도주에 ERROR(/ by zero) 발생
java.lang.ArithmeticException: / by zero
at com.example.demo.service.CalcServiceImpl.divide(CalcServiceImpl.java:24)
at com.example.demo.service.CalcServiceImpl$$FastClassBySpringCGLIB$$ef1e0ecd.invoke(<generated>)
...
반응형
'Backend > Spring Boot' 카테고리의 다른 글
Spring @RestController 를 이용하여 REST API 개발 (0) | 2021.05.17 |
---|---|
JPA Fetch 종류 (LAZY Fetch와 EAGER Fetch) (0) | 2021.03.27 |
Spring Boot 에서 JWT 적용 (Maven 환경) (0) | 2021.01.21 |
SpringBoot, Junit5에서 REST API(Controller) 테스트 (0) | 2021.01.15 |
SpringBoot, Junit5에서 Service 테스트 (1) | 2021.01.13 |