[AOP] Advice
by mignon25어드바이스(Advice) 란?
- 부가 기능
- 조인 포인트에서 수행되는 코드
- Aspect 를 언제 핵심 코드에 적용할지 정의
Advice 순서
- Advice 는 기본적으로 순서를 보장하지 않는다.
- 하나의 Aspect에 여러 어드바이스가 존재하면 순서를 보장받을 수 없다.
- 순서를 지정하고 싶다면?
- @Aspect 적용 단위로,
- 별도의 클래스로 분리하고,
- org.springframework.core.annotation.@Order 애너테이션을 적용
package section2.aop.order.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
// 어드바이스에 순서 적용하기
@Slf4j
@Aspect
public class Aspect5 {
@Aspect
@Order(1) // 파라미터 값으로 순서 적용
public static class LogAspect { // 순서를 적용할 @Aspect 단위로 별도의 클래스 생성
@Around("section2.aop.order.aop.Pointcuts.allOrder()")
public Object doLog(ProceedingJoinPoint joinPoint) {
// Advice doLog() 로직
}
}
@Aspect
@Order(2) // 파라미터 값으로 순서 적용
public static class TxAspect { // 순서를 적용할 @Aspect 단위로 별도의 클래스 생성
@Around("section2.aop.order.aop.Pointcuts.orderAndService()")
public Object doTransaction(ProceedingJoinPoint joinPoint) {
// Advice doTransaction() 로직
}
}
}
다시 말해,
- 순서를 적용할 메서드를 별도의 클래스로 분리하고, 그 클래스에
- @Aspect 를 적용하고
- @Order() 를 적용하고 파라미터 값으로 순서를 넣어준다.
- 그리고 각각의 메서드에 Advice 관련 애너테이션 추가
Advice 종류
- Before
- After returning
- After throwing
- After(finally)
- Around

@Before
- 조인 포인트 실행 이전에 실행
- 타겟 메서드가 실행되기 전에 처리해야할 필요가 있는 부가 기능을 메서드 호출 전에 실행
- @Before Advice 를 구현한 메서드는 일반적으로 리턴타입이 void 이다.
- @Before 를 사용하는 것은 리턴 값에는 별 관심이 없기 때문.
- 리턴 값을 갖더라도 실제 Advice 적용 과정에 아무 영향이 없다.
- @Before Advice 로는 타겟을 호출하는 방식을 제어할 수 없다.
- @Before Advice 를 적용해도 자동으로 다음 타겟이 정상적으로 호출된다.
- JoinPoint 타입의 파라미터를 사용할 수 있다.
- JoinPoint : @Around 메서드의 파라미터로 사용되는 ProceedingJoinPoint 의 슈퍼 인터페이스
- JoinPoint 타입은 메서드 실행지점(조인 포인트)에 대한 정보를 가져올 수는 있지만, 타겟 메서드를 실행하는 proceed() 메서드는 없다.
=> @Before Advice 메서드 종료 시 자동으로 다음 타겟 호출됨
- 주의점 : 예외를 발생시킬 경우, 대상 객체의 메서드를 호출하지 않는다.
@Before("hello.xyz.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
// 조인 포인트 실행 전에 실행할 부가기능 로직
}
파라미터에는 포인트컷 표현식 또는 위의 예제처럼 포인트컷이 적용된 경로 + 메서드 이름를 적어도 된다.
(사용할 포인트 컷들을 모아놓고 사용할 수 있다.)
@AfterReturning
- 타겟 오브젝트의 메서드가 실행(예외 없이 정상 종료된 경우)을 마친 뒤에 실행
- 타겟이 정상 종료된 후에 호출되므로 타겟 메서드의 리턴 값을 참조할 수 있다.
- 리턴 값을 참조할 때는 returning 속성 이용
- value(또는 pointcut) : 포인트컷 표현식
- returning
- 리턴 값 자체를 변경할 수는 없으나, 리천 값이 레퍼런스 타입이라면 참조 오브젝트를 조작하는 것은 가능
- 리턴 값을 전달받을 파라미터의 타입을 구체적으로 지정해주면, 리턴 값의 타입이 일치하는 경우에만 @AfterReturning가 실행된다.
- returning 속성에 사용되는 이름은 메서드의 파라미터 이름과 일치해야 한다.
- JoinPoint 와 함께 사용할 수도 있고, return 값만 파라미터로 사용할 수도 있다.
- JoinPoint 와 함께 사용한다면 JoinPoint 순서가 return값 파라미터보다 앞에 와야 한다.
@AfterReturning(value = "hello.xyz.aop.myPointcut()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result) {
// 타겟 메서드 정상 종료 후 실행할 부가기능 로직
}
근데 리턴 값을 전달받을 파라미터의 타입을 구체적으로 지정해준다는 것이 무슨 말이지...?
@AfterThrowing
- 타겟 메서드가 호출되었을 때 예외가 발생하면 실행
- throwing 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.
- throwing 으로 지정한 파라미터의 타입이 발생한 예외와 일치할 경우에만 어드바이스가 호출된다.
- 모든 예외를 다 전달받으려면 Throwable 로 파라미터 타입을 지정 (?)
@AfterThrowing(value = "hello.xyz.Pointcuts.orderAndService()", throwing = "ex")
public void doThrowing(Joinpoint joinPoint, Exception ex) {
// 타겟 메서드가 예외를 발생시켰을 때 수행할 부가기능 로직
}
@After
- 메서드 실행이 정상 종료되었을 때와 예외가 발생했을 때 모두 실행
- 코드에서 finally 를 사용했을 때와 비슷한 용도
- 반드시 반환되어야 하는 리소스가 있거나, 메서드 실행 결과를 항상 로그로 남겨야 하는 경우 등에 사용
- 리턴 값이나 예외를 직접 전달받을 수는 없다.
@Around
- 프록시를 통해서 타겟 오브젝트의 메서드가 호출되는 전 과정을 모두 담을 수 있는 어드바이스
- 파라미터로 ProceedingJoinPoint 를 사용
- ProceedingJoinPoint 오브젝트의 proceed() 메서드를 통해 타겟 메서드 실행 및 결과 값도 받을 수 있다.
- 가장 강력한 기능을 가진 어드바이스
- 조인 포인트 실행 여부 선택 : joinPoint.proceed()
- 타겟 메서드(조인 포인트) 여러 번 실행
- 전달 값 변환 : joinPoint.proceed(args[])
- 예외 처리 : try, catch, finally
- 리턴 값 변환
@Around("myPointcut()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
// 타겟 메서드 실행 이전 이전에 부가기능 수행
// 핵심 기능 수행
Object output = joinPoint.proceed();
// 타겟 메서드 실행 후 부가기능 수행
return output;
}
적절한 용도에 맞게 Advice를 사용하자.
모든 Advice는 @Around 만으로 전부 처리 가능하다.
그러나 단지 호출 시점에 파라미터 값만 조사해서 로그로 남긴다거나 결과 값만 확인하는 것이 전부라면, ProceedingJoinPoint 파라미터를 선언하고, proceed()를 호출하고, 다시 결과를 리턴하는 것은 별 의미없는 반복 코드가 될 수 있다.
타겟 메서드 전후로 동시에 관여해야 하는 부가기능 로직이 아니라면 적절히 용도에 맞게 다른 어드바이스를 사용하자.
@Before, @After 와 같은 어드바이스의 경우 기능은 적지만, 대신 코드가 단순해지고, 기능이 적어진 만큼 어떤 일을 하는지 명확히 알 수 있게 된다.
좋은 설계는 @Around 만 사용해서 모두 해결하는 것보다는 제약을 두어 역할을 명확하게 하여 실수를 미연에 방지하는 것이다.
'Spring' 카테고리의 다른 글
| Spring MVC 란? (0) | 2023.04.14 |
|---|---|
| [빈 스코프] 빈 스코프와 프로토타입 스코프 (0) | 2023.04.13 |
| [AOP] JoinPoint (0) | 2023.04.12 |
| [AOP] Pointcut (0) | 2023.04.12 |
| 조회한 빈이 모두 필요할 때 - List, Map 사용 (0) | 2023.04.12 |
블로그의 정보
Mignon'S Dev Log
mignon25