[TIL] 23.04.30 스프링 AOP ( Aspect-Oriented Programming )

hyewon jeong·2023년 4월 30일
0

TIL

목록 보기
124/138

1. AOP ( Aspect-Oriented Programming )

  • 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리
  • AOP는 Aspect Oriented Programming의 약자로,
    여러 객체에 공통으로 적용할 수 있는 기능을 분리해 재사용성을 높여주는 기법으로써 다음과 같은 특징을 지님

1-1. 핵심 기능에 공통 기능을 삽입하며 다음과 같은 방법이 존재

1-1-1. 컴파일 시점에 코드에 공통 기능을 삽입

1-1-2. 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입

1-1-3. 런타임에 프록시 객체를 생성해서 공통 기능을 삽입

이 중, 스프링이 제공하는 AOP 방식은 프록시를 이용한 세 번째 방식

1) 스프링 AOP는 프록시 객체를 자동으로 생성해줌

AOP 적용 전 의존관계

AOP 적용 후 의존관계


실제 Proxy가 주입되는지 콘솔에 출력해서 확인하기

  • CGLIB 라는 객체로 출력되는 것을 확인 할 수 있다.
    CG 라이브러리는 바이트코드를 조작하는 라이브러리로 이를 통해 프록시 객체 생성한다.

2) AOP의 공통 기능을 Aspect라 칭하며, 아래와 같은 주요 용어가 존재

3) 핵심 관심 사항을 깔끔하게 유지할 수 있다.

4) 변경이 필요하면 이 로직만 변경하면 된다.

5) 원하는 적용 대상을 선택할 수 있다.

2. 관련 용어 및 메서드 정리

@Aspect//aop 적용
@Component // 컴포넌트 스캔을 사용해도 되지만 springConfig 에 빈설정정보를 통해 빈등록하는 것이 한눈에 aop정보를 이용해 aop를 하는 구나 알 수 있다.
public class TImeTraceAop {

  //@Around("execution(* hello 2.hello..*(..)") 
  public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    System.out.println("START:" + joinPoint.toString());//어떤 메서드들이 호출되는 확인
    try {
      return joinPoint.proceed();//프록시 객체가 아닌 실제 대상 객체의 메서드 호출 한다. 

    } finally {
      long finish = System.currentTimeMillis();
      long timeMs = finish - start;
      System.out.println("Finish:" + joinPoint.toString() + " " + timeMs + "ms");
    }

  }
}

2-1. @Aspect

Aspect: 여러 객체에 공통으로 적용되는 기능으로
@Aspect 어노테이션을 이용하여 공통 기능을 제공하는 클래스 설정

  • 어노테이션을 적용한 클래스는 Advice와 Pointcut을 함께 제공

2-1. Advice 란?

언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의하는데 ,
Advice는 애플리케이션의 비즈니스 로직과는 별개로, 재사용 가능한 모듈화된 코드조각이다. Advice를 이용하면 애플리케이션의 비즈니스 로직과는 독립적으로, 여러 지점에서 재사용 가능한 로직을 작성할 수 있습니다.

  • Before Advice: 대상 메서드가 실행되기 전에 실행됩니다.

  • After Returning Advice: 대상 메서드가 정상적으로 실행되고 반환값이 있는 경우에 실행됩니다.

  • After Throwing Advice: 대상 메서드가 예외를 던지는 경우에 실행됩니다.

  • After Finally Advice: 대상 메서드가 실행된 후 항상 실행됩니다.

  • ⭐️ Around Advice: 대상 메서드의 실행 전과 후 모두 실행됩니다. 대상 메서드를 직접 호출하는 것과 비슷하게 작동하며, 대상 메서드를 실행하는 ProceedingJoinPoint 객체를 파라미터로 전달받아 직접 실행 결정을 할 수 있다. Advice가 대상 메서드의 실행을 제어할 수 있다.

//Around Advice 적용
 @Around("execution(* hello2.hello..*(..)") 
 //() 에 타겟팅된 위치에 있는 대상 메서드의 실행 전과 후에 모두 실행됨

2-2-1. execution 명시자 표현 방식

  • Aspect를 적용할 위치를 지정하기 위한 Pointcut 설정
   @Pointcut("execution(public * cahp07..*(..))")
    private void public Target(){
    }
  • 스프링 AOP는 public 매서드에만 적용할 수 있기 때문에, 사실상 public만 가능

  • execution(수식어패턴? 리턴타입패턴 클래스이름패턴?매서드이름패턴(파라미터패턴)) 방식으로 사용

수식어패턴은 생략 가능하며 public, protected등이 올 수 있음
리턴타입패턴은 리턴 타입을 명시
클래스이름패턴, 매서드이름패턴은 클래스 이름 및 매서드 이름을 패턴으로 명시
파라미터패턴은 매칭될 파라미터에서 대해 명시

"..*"은 패키지 내의 모든 하위 패키지를 포함하는 패턴입니다.

예를 들어, "@Pointcut("execution(public * chap07..*(..))")"은 "chap07" 패키지와 "chap07" 패키지의 모든 하위 패키지에 속한 모든 public 메서드를 Pointcut으로 설정합니다.

".."은 0개 이상의 하위 패키지를 의미하며, "*"은 모든 메서드를 의미합니다. 따라서 위의 Pointcut은 "chap07" 패키지와 그 하위 패키지에 속한 모든 public 메서드를 대상으로 합니다.


이 중 널리 사용되는 것은 Around Advice이며,
그 이유는 대상 객체의 매서드를 실행하기 전/후, 익셉션 발생등 다양한 시점에 원하는 기능을 삽입할 수 있기 때문이다.

2-3. proceedingJoinPoint 메서드?

  • Advice가 적용될 지점은 Join Point(조인 포인트)라고 부르며, Join Point의 실행을 계속 진행하기 위한 메서드를 "proceedingJointPoint"라고 합니다.
  • 즉 Advice가 적용된 메서드의 실행을 계속해서 진행하기 위한 지점을 가리키는 용어이다.
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {}

2-4. joinPoint.proceed()

  • 매서드를 실행시, 프록시 객체가 아닌 실제 대상 객체의 메서드 호출 한다.
  • 그래서 이 코드 전후로 공통기능을 위한 코드를 위치 시켜야한다.
    //ExeTimeAspect
    // 공통 기능을 제공하는 클래스 설정
    @Aspect
    public class ExeTimeAspect {

            // 공통 기능을 적용할 Pointcut 설정
        @Pointcut("execution(public * chap07..*(..))")
        private void publicTarget() {
        }

            //  publicTarget() 매서드에 정의한 Pointuct에 공통 기능을 적용
        @Around("publicTarget()")
        public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
            long start = System.nanoTime();
            try {
                            // proceed() 매서드를 사용해서 실제 대상 객체의 매서드를 호출
                Object result = joinPoint.proceed();
                return result;
            } finally {
                System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
                ...
            }
        }

    }

2-4. Joinpoint

  • Advice를 적용 가능한 지점을 의미
    (매서드 호출, 필드 값 변경 등, 스프링은 프록시를 이용해 AOP를 구현하기 때문에 매서드 호출에 대한 Joinpoint만 지원)

2-5. Pointcut

  • Joinpoint의 부분 집합, 실제 Advice가 적용되는 Joinpoint를 나타냄
  • @Pointcut이 붙은 매서드는 다음의 조건을 일반적(반드시?)으로 만족해야 함

void 리턴 타입
비어있는 매서드
어떤 throws 문도 포함하지 않음

2-5-1. Pointcut 표현식

Pointcut을 이용하면 Advice 메소드가 적용될 비즈니스 메소드를 정확하게 필터링 할 수 있다.

지시자(PCD, AspectJ pointcut designators)의 종류

  • execution : 가장 정교한 Pointcut을 만들수 있고, 리턴타입 패키지경로 클래스명 메소드명(매개변수)
  • within : 타입패턴 내에 해당하는 모든 것들을 Pointcut으로 설정
   // 패키지의 Controller 라는 이름으로 끝나는 패키지에 모두 적용 (매서드만)
    @Pointcut("within(*..*Controller)")
    private void cut(){}
  • bean : bean이름으로 Pointcut

3. 스프링 AOP 구현 순서

의존성 추가

    implementation 'org.springframework.boot:spring-boot-starter-aop'

1) Aspect로 사용할 클래스에 @Aspect 어노테이션을 붙임
2) @Pointcut 어노테이션으로 공통 기능을 적용할 Pointcut을 정의
3) 공통 기능을 구현한 매서드에 @Around 어노테이션을 적용

4. proxy

스프링은 Aspect의 적용 대상(Target)이 되는 객체에 대한 Proxy를 만들어 제공한다.
대상 객체(Target)를 사용하는 코드는 대상 객체(Target)를 Proxy를 통해서 간접적으로 접근하게 되며, Proxy는 공통기능(Advice)을 실행한 뒤 대상객체(Target)의 실제 메서드를 호출하거나 또는 대상객체(Target)의 실제 메소드가 호출된 뒤 공통기능(Advice)을 실행한다.

Proxy

Proxy는 타겟을 감싸서 요청을 대신 받아주는 랩핑 클래스이다.
Spring에서는 Proxy를 이용해 객체지향의 5대원칙 중 하나인 OCP를 적용하고 있다.
Open-Close Principal : 개방폐쇄의 원칙
'소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고,
수정에 대해서는 닫혀 있어야 한다.'는 프로그래밍 원칙

참고
김영한 인프런 강의
https://owin2828.github.io/devlog/2019/12/30/spring-7.html#1-3-about-advice
https://velog.io/@gillog/AOP%EA%B4%80%EC%A0%90-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

profile
개발자꿈나무

0개의 댓글