Spring
Spring
Spring 관점 지향 프로그래밍

AOP(Aspect Opriented Programming) 관점 지향 프로그래밍

스프링 어플리케이션은 대부분 특별한 경우를 제외하고는 MVC 웹 어플리케이션에서는 Web Layer, Business Layer, Data Layer로 정의.

  • Web Layer : REST API를 제공하며, Client 중심의 로직 적용
  • Business Layer : 내부 정책에 따른 logic을 개발하며, 주로 해당 부분을 개발
  • Data Layer : 데이터 베이스 및 외부와의 연동을 처리
Annotation의미
@AspectAOP를 정의하는 Class에 사용
@PointcutAOP가 적용될 JoinPoint를 선언
@Before메서드 실행 전에 적용
@After메서드 실행 후에 적용
@AfterReturning메서드가 호출 성공 실행 시 적용
@AfterThrowing메서드가 호출 실패 예외 발생 시 적용
@Around메서드 실행 전, 후 또는 예외 발생 시 적용

AOP 적용

PointCut 설정

어디에 적용할지 선언해줘야 함

@Aspect
@Component // Spring으로부터 관리되게 해준다.
public class TimerAop {
  // Spring에서 관리하는 Bean들에게만 사용할 수 있다.
  // 만약 Bean이 아닌 다른 클래스에 사용하고 싶다면 aspectj를 사용해야 한다.
  @Pointcut(value = "within(com.example.filter.controller.UserApiController)")
  public void timerPointCut() {
 
  } 
 
  ... other methods
}

상황에 맞는 Annotation 사용

Before
  @Before(value = "timerPointCut()")
  public void before(JoinPoint joinPoint) {
    System.out.println("========== Before ==========");
  }
After
  @After(value = "timerPointCut()")
  public void after(JoinPoint joinPoint) {
    System.out.println("========== After ==========");
  }
AfterReturning
  @AfterReturning(value = "timerPointCut()", returning = "result")
  public void afterReturning(JoinPoint joinPoint, Object result/* 결과 값이 여기로 들어온다 */) {
    System.out.println("========== After Returning ==========");
    System.out.println(MessageFormat.format("========== result : {0} ==========", result));
    System.out.println("========== After Returning ==========");
  }
AfterThrowing
  @AfterThrowing(value = "timerPointCut()", throwing = "ex")
  public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
    System.out.println("========== After Throwing ==========");
  }
Around
  @Around(value = "timerPointCut()") // 어떤 Pointcut 식을 사용할 것인지 지정해줘야 한다.
  public void around(ProceedingJoinPoint joinPoint/* 실행전 args 등이 들어감*/) throws Throwable {
    System.out.println("메서드 실행 이전");
 
    Arrays.stream(joinPoint.getArgs()).forEach(
        it -> {
          if(it instanceof UserRequest) {
            var tempUser = (UserRequest) it;
            String phoneNumber = tempUser.getPhoneNumber().replace("-", "");;
            tempUser.setPhoneNumber(phoneNumber);
          }
 
          System.out.println(it);
        }
    );
 
    var stopWatch = new StopWatch();
    stopWatch.start();
    var newObjs = Arrays.asList(new UserRequest());
    // 메서드를 실행시키는 실체를 감싼 부분이다.
    joinPoint.proceed(newObjs.toArray()); // 전부 null로 나옴
    System.out.println("메서드 실행 이후");
 
    stopWatch.stop();
    System.out.println("총 소요 시간 : " + stopWatch.getTotalTimeMillis() + "ms");
  }

PointCut 지시자

PCD의미
execution메서드 실행 시점
within특정 경로의 타입을 기준으로 지정
this특정 타입의 객체를 기준으로 지정
target특정 타입의 객체를 기준으로 지정
args특정 인자를 받는 메서드 호출 시점
@within특정 경로의 Annotation을 기준
@target특정 Annotation이 붙은 객체를 기준으로 지정
@args특정 Annotation의 파라미터를 가지는 메서드 호출 시점
@annotation메서드의 Annotation을 기준
bean특정 Bean의 메서드 호출 시점

PCD 예시

"execution([접근제한자-생략가능][리턴 타입][클래스 이름]메서드 이름)"

참고자료

참고 PDF (opens in a new tab)