memostack
article thumbnail
블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.github.io/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
반응형

1. AOP 구현

AOP 개념은 아래 글 참고

2021.03.11 - [Spring] - Spring의 AOP 개념 (Aspect Oriented Programming)

 

Spring의 AOP (Aspect Oriented Programming)

AOP AOP는 Aspect Oriented Programming 의 약자로, 번역하면 관점 지향 프로그래밍이다. AOP는 주 비지니스 로직 앞, 뒤로 부가적인 기능을 추가하고 싶을때 사용하는데 예를들어, 로그처리, 보안처리, DB

memostack.tistory.com

 

1.1. 의존성 추가

<html />
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

 

1.2. 서비스(비즈니스 로직) 구현

테스트를 위한 비즈니스 로직을 구현

  • AOP 구현에 집중하기 위해, 단순히 2개 정수로 사칙연산하는 메소드를 생성
<java />
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); }
<java />
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; } }

 

1.3. Aspect 구현

로그를 남기는 Aspect를 구현한다.

  • @Around 로 앞, 뒤로 실행하는 Advice를 구현
<java />
@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; } }

 

1.4. 실행

메소드 앞뒤로 실행 됨

<java />
@SpringBootTest public class AopTest { @Autowired private CalcService calcService; @Test @DisplayName("AOP - Around 테스트") void aopAroundTest() { calcService.sum(3, 5); } }
<java />
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 사용)

실행순서

 

1.5. @Before 예시

subtract() 빼기 연산 메소드 전에 로그를 찍어주는 Advice 추가

<java />
@Aspect @Component public class LogAspect { // ... @Pointcut("execution(public * com.example.demo.service.CalcService.subtract(..))") private void subtractTarget() { } @Before("subtractTarget()") public void subtractBeforeAdvice() { logger.info("빼기 연산을 수행합니다."); } }

 

그리고, 실행

<java />
@Test @DisplayName("AOP - Before 테스트") void aopBeforeTest() { calcService.subtract(100, 20); }
<java />
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

 

1.6. @After 예시

multiply() 메소드 실행 후에 로그를 찍어주는 Advice 추가

<java />
@Aspect @Component public class LogAspect { // ... @Pointcut("execution(public * com.example.demo.service.CalcService.multiply(..))") private void multiplyTarget() { } @After("multiplyTarget()") public void multiplyAfterAdvice() { logger.info("곱하기 연산이 끝났습니다"); } }

 

그리고, 실행

<java />
@Test @DisplayName("AOP - After 테스트") void aopAfterTest() { calcService.multiply(3, 10); }
<java />
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

 

1.7. @AfterRenturnning 예제

메소드 실행하고 return까지 반환한 뒤 실행

<java />
@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); } }

 

그리고, 실행

<java />
@Test @DisplayName("AOP - AfterReturning 테스트") void aopAfterReturningTest() { calcService.sum(10, 2); }
<java />
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

 

1.8. @AfterThrowing 예제

0으로 나누는것이 불가능하기 때문에 Exception 발생, 그때 실행

<java />
@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()); } }

 

그리고 실행

<java />
@Test @DisplayName("AOP - AfterReturning 테스트") void aopAfterThrowingTest() { calcService.divide(10, 0); }
<java />
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>) ...
반응형
블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.github.io/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
profile

memostack

@bluemiv_mm

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!