Spring

스프링 aop

yoon9 2022. 12. 13. 01:54

스프링 AOP는 Aspect Oriented Programming의 약자이며, 관점 지향 프로그래밍입니다.

관점 지향 프로그래밍의 "관점 지향이란"   크게 핵심적인 관점과 부가적인 관점으로 나누어서 각각 모듈화를 하겠다는 것입니다.(관심사 분리)

 

예를 들어보자.

한 개발자가 개발 중에 있는데, 갑자기 모든 호출되는 메서드의 소요시간을 알고 싶다며, 모든 메서드에 소요시간을 구하는 로직을 구현을 해야 한다..

 

메서드가.... 1만 개라면..? 할 수야 있겠지만 엄청나게 오래 걸릴 것이다..

이런 식으로 개발을 하게 된다면..? 

  • 중복 코드 발생 -> 모든 코드에 똑같이 반복되는 코드가 생긴다. 흩어진 관심사(Crosscutting Concerns)
  • 핵심 로직과 부가적인 로직으로 인하여 service단에서 핵심 로직에 말고도 또 부가적인 로직에도 신경을 써야 함.

 

 

아까 위에서 말했다시피 핵심 관심사와 부가적인 관심사를 분리를 하여 모듈화를 하고 관리를 하면 

좀 더 객체지향스러워(?) 진다. 

 

 

AOP의 용어 정리

 

  • Target
    • 핵심기능을 가지고 있는 모듈로써 부가기능을 부여할 대상이다.(핵심 로직이라고 생각하면 된다.)
  • Advice 
    • 실질적으로 부가기능을 담은 구현체이다(실질적인 부가기능이다)
  • PointCut
    • Advice를 적용할 타깃의 메서드를 선정하는 방법(부가기능을 "어디에 적용할 것인지"->모든핵심 로직에 넣을 것인지 해당원하는 핵심로직에 넣을 것인지)
  • JoinPoint
    • Advice 적용될 수 있는 위치 (부가기능을 핵심 로직이 실행 전, 실행 후 등등 Adivce를 실행) 
  • Aspect 
    • Adivce+PointCut을 합친 개념이다.
    • 싱글톤으로 존재한다.
    • AOP 개념을 적용하면 핵심기능 코드 사이에 침투된 부가기능을 독립적인 애스펙트로 구분해 낼 수 있다.
    • 구분된 부가기능 애스펙트를 런타임 시에 필요한 위치에 동적으로 참여하게 할 수 있다.
  • Proxy
    • 프락시 기반이다.
    • 스프링에서는 Aop동작을 Proxy가 가로채어서 Advice의 부가기능을 실행 후 Target의 핵심기능을 실행한다.
  • Weaving
    • 포인트 컷에 의해서 결정된 타깃의 조인 포인트에 부가기능(advice)을 삽입하는 과정을 뜻한다.
    • AOP가 핵심기능(타깃)의 코드에 영향을 주지 않으면서 필요한 부가기능(advice)을 추가할 수 있도록 해주는 핵심적인 처리과정이다.

joinPoint

  • @Around (메서드 실행 전후)
    • 타깃 메소드 호출전과 후에 Advice기능을 수행한다.
  • @Before (이전)
    • Advice 타깃 메소드가 호출되기 전에 Advice 기능을 수행
  • @After (이후)
    • 타깃 메소드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메소드가 완료 되면 Advice기능을 수행
    • 무조건 실행된다고 보면된다.
  • @AfterReturning (정상적 반환 이후)
    • 타겟 메소드가 성공적으로 결과값을 반환 후에 Advice기능을 수행
  • @AfterThrowing (예외 발생 이후)
    • 타겟 메소드가 수행 중 예외를 던지게 되면 Advice기능을 수행

 

PointCut 종류

 

  • args()
    • 메소드의 인자가 타겟 명세에 포함된 타입일 경우
    • ex) args(java.lang.Long) 
  • @args()
    • 메소드의 인자가 타겟 명세에 포함된 어노테이션 타입을 갖는 경우
  • execution()
    • 접근 제한자, 리턴 타입, 인자 타입, 클래스/인터페이스, 메서드명, 파라미터 타입, 예외 타입 등을 전부 조합 가능한 가장 세심한 지정자
    • 대부분 이것을 사용하며, 세세하게 다 적용될 수 있음.
  • within()
    • execution 지정자에서 클래스/인터페이스까지만 적용된 경우
    • 즉, 클래스 혹은 인터페이스 단위까지만 범위 지정이 가능하다.
    • exectuion 지정자보다는 덜 세세함.
  • @within()
    • 주어진 어노테이션을 사용하는 타입으로 선언된 메서드
  • this()
    • 타깃 메소드가 지정된 빈 타입의 인스턴스인 경우
  • target()
    • this와 유사하지만 빈 타입이 아닌 타입의 인스턴스인 경우
  • @target()
    • 타깃 메소드를 실행하는 객체의 클래스가 타겟 명세에 지정된 타입의 어노테이션이 있는 경우
  • @annotation
    • 타겟 메서드에 특정 어노테이션이 지정된 경우
    • 내가 지정한 부분만 어노테이션을 사용하여서 Advice를 적용할 수 있음.

 

 

위에 예를 들었다시피 메서드 별로 시간 측정을 하기를 원한다.

 

모든 메서드는 아니고 Controller, Service Layer의 Method들의 시간 측정을 원한다.

하는 방법은 여러 가지이다 @annotation을 지정하여 할 수도 있지만, @annotation은 특정 어떠한 타깃에만 소수로 적용될 때 좋은 것 같다.

 

내가 적용한 방법은 타깃의 핵심 로직 메서드를 호출 전에 time을 찍고 핵심로직 수행 후에도 time을 찍어서 측정을 할 것이기 때문에 @Around advice를 적용을 하여서 구현한다.

@Aspect
@Component
@Slf4j
public class TimeTraceAop {

    //컨트롤러 포인트컷
    @Pointcut("within(com.example.yoonFood.controller..*)")
    private void getControllerTime(){

    }
    //서비스 포인트컷 
    @Pointcut("within(com.example.yoonFood.service..*)")
        private void getServiceTime(){

    }

    @Around("getControllerTime() || getServiceTime()")
    public Object methodControllerTime(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;
        long start = System.currentTimeMillis();
        String methodName = proceedingJoinPoint.getSignature().getName();
        log.info("Start:  실행 메서드 :{} ",methodName);


        try {
            result = proceedingJoinPoint.proceed();
            long end = System.currentTimeMillis();
            log.info("End :  실행 메서드 :{} ,수행시간 : {}",methodName ,end-start+"ms" );
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return result;
    }

}

측정을 하여서 log를 띄우는 건 개발자 스타일마다 다른 것이고.. 나는 메서드명을 띄었다 아니면 ToString을 이용하여서 해도 된다 (이건 개발자 맘!)

 

@Around의 포인트 컷은 @PointCut으로 위에화 같이 따로 분리하여서 설정하여도 되고

ex) @Around("execution(com.example.yoonFood..*)")등으로 바로 설정해도 된다.

 

포인트 컷 지정자는 여러 가지 조합이 많기 때문에 자기가 원하는 스타일대로 구글링을 해서 사용을 하면될 것 같다..

 

그리고 측정 중에 Controller와 Service Layer는 연관관계로 묶여있기 때문에(의존성이 주입되어 있기 때문에)

대부분의 Controller를 호출을 하면, Service의 method를 호출을 한다.

그래서 Controller 호출 -> Service호출 -> Service 끝-> Controller끝 이런 식으로 진행이 된다.

 

 

추가로 

구글링을 해서 인터넷에 자료를 검색을 하는 중에 대부분이 @Around,@Before,@After와 같이 joinPoint를

Advice라고 설명을 하는 것 같다.

Advice는 부가기능을 포함한 그 자체인데... JoinPoint 뜻도 Advice를 어디에 적용을 하는가에.. 뜻인데..

이것 때메 이해 하기가 쫌 어려웠다

아님 말고 이상 끝!

 

 

Reference

https://jojoldu.tistory.com/71?category=635883