우와한 개발자

[Spring] AOP 적용 – 어노테이션 방식 (@Aspect, @Pointcut, @Before, @Around 등)

by 우와한개발자

[ AOP 적용 - 어노테이션(Annotation) 방식 ]

용어 쉬운 설명 예시
Aspect 부가 기능을 모듈화한 클래스 MemberLog
Advice 부가 기능 메서드 MemberLog.logBefore()
Target 핵심 기능을 가진 클래스 MemberService
JoinPoint Advice를 적용할 수 있는 모든 시점(Spring AOP는 메서드 실행 지점) join(), login(), update() 실행 시점 전부
Pointcut JoinPoint 중 Advice를 실제 적용할 위치를 선별하는 표현식 execution(* MemberService.join(..))
Proxy Target을 감싸 Advice를 대신 실행하는 클래스 MemberService$$Proxy
Weaving Pointcut 기준으로 Target에 Advice를 결합하는 과정 join() 호출 시 logBefore()가 자동 실행

 

1. AOP 설정

  • 클래스에 어노테이션을 붙여 AOP를 적용하는 방식
  • @Aspect를 인식하려면 설정이 필요함

 

1) XML로 어노테이션 AOP 활성화

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- @Aspect 어노테이션 활성화 -->
    <aop:aspectj-autoproxy/>

    <!-- Aspect 클래스를 Bean으로 등록 -->
    <bean id="memberLog" class="com.example.myapp.MemberLog"/>
    <bean id="memberService" class="com.example.myapp.MemberService"/>

</beans>

 

2) Java Config로 어노테이션 AOP 활성화

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

 

2. AOP 적용 어노테이션

어노테이션 설명
@Aspect 해당 클래스를 Aspect로 선언
@Pointcut Pointcut 표현식 정의
@Before Before Advice 적용
@After After Advice 적용
@AfterReturning After Returning Advice 적용
@AfterThrowing After Throwing Advice 적용
@Around Around Advice 적용

 

1) @Aspect

  • 해당 클래스를 Aspect로 선언
  • @Component와 함께 사용해야 Bean으로 등록됨
@Aspect
@Component
public class MemberLog {
}

 

2) @Pointcut

  • Pointcut 표현식을 메서드로 정의해 재사용할 수 있음
  • Advice 어노테이션에 표현식을 직접 작성하면 생략 가능
속성 설명
value Pointcut 표현식 (필수)
@Aspect
@Component
public class MemberLog {

    @Pointcut("execution(* com.example.MemberService.*(..))")
    public void memberPointcut() {} // 메서드 이름이 Pointcut 식별자가 됨
}

 

3) @Before / @After

속성 설명
value Pointcut 표현식 또는 @Pointcut 메서드명
@Before("memberPointcut()")
public void logBefore() {
    System.out.println("메서드 실행 전 로그");
}

@After("memberPointcut()")
public void logAfter() {
    System.out.println("메서드 실행 후 로그");
}

 

(1) JoinPoint 파라미터

  • Advice 어노테이션 메서드에서 첫 번째 파라미터로 JoinPoint를 받을 수 있음
  • @Around는 반드시 ProceedingJoinPoint를 첫 번째 파라미터로 받아야 함 (필수)
  • ProceedingJoinPoint는 JoinPoint를 상속한 타입으로 proceed()로 Target 메서드를 직접 실행할 수 있어서 @Around에서만 사용함
Advice JoinPoint 파라미터 타입
@Before / @After / @AfterReturning / @AfterThrowing 선택 JoinPoint
@Around 필수 ProceedingJoinPoint

 

(2) JoinPoint 주요 메서드

메서드 설명
jp.getSignature() 실행된 메서드 정보
jp.getSignature().getName() 실행된 메서드명
jp.getArgs() 메서드에 전달된 파라미터
jp.getTarget() Target 객체
@Before("memberPointcut()")
public void logBefore(JoinPoint jp) {
    System.out.println("메서드명 : " + jp.getSignature().getName());
    System.out.println("파라미터 : " + Arrays.toString(jp.getArgs()));
}

 

4) @AfterReturning

속성 설명
value Pointcut 표현식 또는 @Pointcut 메서드명
returning 반환값을 받을 파라미터명
@AfterReturning(value = "memberPointcut()", returning = "result")
public void logReturn(Object result) {
    System.out.println("반환값 : " + result);
}

 

5) @AfterThrowing

속성 설명
value Pointcut 표현식 또는 @Pointcut 메서드명
throwing 발생한 예외를 받을 파라미터명
@AfterThrowing(value = "memberPointcut()", throwing = "ex")
public void logException(Exception ex) {
    System.out.println("예외 발생 : " + ex.getMessage());
}

 

6) @Around

  • 첫 번째 파라미터가 반드시 ProceedingJoinPoint이어야 함
속성 설명
value Pointcut 표현식 또는 @Pointcut 메서드명
@Around("memberPointcut()")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("실행 전");
    Object result = pjp.proceed(); // Target 메서드 실행
    System.out.println("실행 후");
    return result;
}

전체 예시

더보기
@Aspect
@Component
public class MemberLog {

    // Pointcut 정의
    @Pointcut("execution(* com.example.MemberService.*(..))")
    public void memberPointcut() {}

    @Before("memberPointcut()")
    public void logBefore() {
        System.out.println("메서드 실행 전 로그");
    }

    @AfterReturning(value = "memberPointcut()", returning = "result")
    public void logReturn(Object result) {
        System.out.println("반환값 : " + result);
    }

    @AfterThrowing(value = "memberPointcut()", throwing = "ex")
    public void logException(Exception ex) {
        System.out.println("예외 발생 : " + ex.getMessage());
    }

    @Around("memberPointcut()")
    public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("실행 전");
        Object result = pjp.proceed();
        System.out.println("실행 후");
        return result;
    }
}

 

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기