본문 바로가기

개발중/Spring

[Spring] '스프링 AOP (Spring AOP)' 가 뭐냐고 물어보면 바로 대답해주기

728x90
반응형

 

AOP 관련 용어

  • Aspect
    • 흩어진 관심사를 모듈화 한 것. 
  • Target
    • Aspect를 적용하는 곳. 클래스, 메서드 등..
  • Advice
    • 실질적으로 어떤 일을 해야 할 지에 대한 것, 실질적인 부가기능을 담은 구현체
  • Join Point 
    • Advice가 적용될 위치 혹은 끼어들 수 있는 시점. 메서드 진입 시점, 생성자 호줄 시점, 필드에서 꺼내올 시점 등 끼어들 시점을 의미.
    • 참고로 스프링에서 Join Point는 언제나 메서드 실행 시점을 의미 한다.
  • Point Cut
    • Join Point의 상세한 스펙을 정의한 것. "A란 메서드의 진입 시점에 호출할 것"처럼 구체적으로 Advice가 실행될 시점을 정함.

 

AOP 적용 방법

1. 컴파일 타임 적용

-> 컴파일 시점에 바이트 코드를 조작하여 AOP가 적용된 바이트 코드를 생성하는 방법.

 

2. 로드 타임 적용

-> 순수하게 컴파일한 뒤, 클래스를 로딩하는 시점에 클래스 정보를 변경하는 방법

 

3. 런타임 적용

-> 스프링 AOP가 주로 사용하는 방법. A라는 클래스 타입의 Bean을 만들 때 A 타입의 Proxy Bean을 만들어 Proxy Bean이 Aspect 코드를 추가하여 동작하는 방법. 

 

스프링 AOP

  • 스프링에서 제공하는 스프링 AOP는 프락시 기반의 AOP 구현체이다.
  • 프록시 객체를 사용하는 것은 접근 제어 및 부가 기능을 추가하기 위해서이다.
  • 스프링 AOP는 스프링 Bean에만 적용할 수 있다.
  • 모든 AOP 기능을 제공하는 것이 목적이 아닌, 중복 코드, 프록시 클래스 작성의 번거로움 등 흔한 문제를 해결하기 위한 솔루션을 제공하는 것이 목적이다.
  • 스프링 AOP는 순수 자바로 구현되었기 때문에 특별한 컴파일 과정이 필요하지 않다.

 

스프링 AOP 구현

Spring AOP를 사용하기 위해서는 의존성을 추가해줘야 한다.

 

maven

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

gradle

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

 

 

지금부터 구현할 기능은 타겟 메서드의 실행 시간을 측정해주는 로직이다.

 

@Component
@Aspect
public class PerfAspect {

    @Around("execution(* com.example..*.EventService.*(..))")
    public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
        long begin = System.currentTimeMillis();
        Object reVal = pjp.proceed();
        System.out.println(System.currentTimeMillis() - begin);
        return reVal;
    }
}

 

스프링 AOP는 Bean에서만 동작한다고 했다.

따라서 @Component 어노테이션 등을 통해 스프링 Bean으로 등록해준 뒤 사용해야 한다.

@Aspect 어노테이션을 붙이면 해당 클래스가 Aspect라는 것을 명시해준다. 

 

logPerf() 메서드는 @Around 어노테이션의 execution을 통해 Advice를 적용할 범위를 지정해줄 수 있다.

위의 코드를 예시로 들자면 com.example 밑의 모든 클래스에 적용하고,

EventService 밑의 모든 메서드에 적용하라는 뜻이다.

 

위와 같은 경로 지정 방식이 아닌 특정 어노테이션이 붙은 포인트에 해당 Aspect를 실행할 수도 있다.

 

@Component
@Aspect
public class PerfAspect {

  @Around("@annotation(PerfLogging)")
  public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
    long begin = System.currentTimeMillis();
    Object retVal = pjp.proceed();
    System.out.println(System.currentTimeMillis() - begin);
    return retVal;
  }
}

 

위와 같이 @Around 어노테이션에 @annotation(PerfLogging)처럼 적용될 어노테이션을 명시할 수 있다. 그럼 해당 메서드를 적용시킬 특정 메서드에 @PerfLogging 어노테이션을 붙여주기만 하면 logPerf() 기능이 동작한다. 

 

혹은 특정 Bean 전체에 해당 기능을 적용시킬 수도 있다.

 

// Aspect 정의
@Component
@Aspect
public class PerfAspect {

  // 빈이 가지고있는 모든 퍼블릭 메쏘드
  @Around("bean(simpleServiceEvent)")
  public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
    long begin = System.currentTimeMillis();
    Object retVal = pjp.proceed();
    System.out.println(System.currentTimeMillis() - begin);
    return retVal;
  }
}

 

위와 같이 @Around 어노테이션에 bean(simpleServcieEvent)처럼 적용될 빈을 명시할 수 있다.

그럼 해당 빈이 가지고 있는 모든 public 메서드에 해당 기능이 적용된다.

 

 

@Around 외에 타겟 메서드의 Aspect 실행 시점을 지정할 수 있는 어노테이션은 다음과 같은 것들이 있다.

 

  • @Before : Advice 타겟 메서드가 호출되기 전에 Advice 기능 수행
  • @After : 타겟 메서드의 결과에 관계없이 타겟 메서드과 완료되면 Advice 기능 수행
  • @AfterRunning : 타겟 메서드가 성공적으로 결과값을 반환 한 후에 Advice 기능 수행
  • @AfterThrowing : 타겟 메서드가 수행 중 예외를 던지면 Advice 기능 수행
  • @Around : Advice가 타겟 메서드를 감싸 타겟 메서드 호출 전, 후에 Advice 기능 수행

 

 

 

728x90
반응형