AOP 실습
AOP에서 포인트컷(Pointcut)을 여러 개 등록하고 해당 경우가 흔한 상황은 매우 일반적입니다. 포인트컷은 특정 메서드 또는 메서드 그룹을 선택하는 데 사용되며, 각 포인트컷은 다른 관심사(concern)와 결합하여 특정 메서드에 대한 어드바이스(Advice)를 적용합니다.
주로 CUD(Create, Update, Delete)와 관련된 포인트컷에서는 데이터베이스에 직접적인 변화가 발생하기 때문에 데이터의 검증, 보안, 트랜잭션 관리 등이 필요합니다. 이러한 관심사를 포함한 어드바이스를 CUD 관련 메서드에 적용하게 됩니다.
반면에 R(Read)은 데이터베이스에 변화를 주지 않고 데이터에 접근하는데 사용됩니다. 따라서 R과 관련된 포인트컷에서는 주로 데이터에 접근 권한 검사와 같은 보안 관련 로직이 필요할 뿐, 트랜잭션 관리나 데이터의 변경 검증과 같은 공통 로직은 상대적으로 적게 필요합니다.
AOP에서는 이러한 상황을 고려하여 포인트컷에 어드바이스를 적용할 수 있습니다. 일부 포인트컷에만 어드바이스가 동작하도록 설정할 수 있으며, 이를 통해 각 포인트컷에 필요한 관심사를 조합하여 적용할 수 있습니다. 이렇게 하면 코드를 더 모듈화하고 재사용 가능한 어드바이스를 작성할 수 있습니다.
어드바이스 동작 시점
- Before Advice (이전 어드바이스) : 특정 메서드가 실행되기 이전에 실행됩니다. 예를 들어, 메서드 호출 전에 로그를 기록하거나 인증/권한 확인 등의 작업을 수행할 수 있습니다.
- After Returning Advice (정상 반환 이후 어드바이스) : 특정 메서드가 정상적으로 반환된 후에 실행됩니다. 메서드가 예외를 던지지 않고 성공적으로 실행된 경우에 실행됩니다. 결과를 가공하거나 로깅하는 데 사용될 수 있습니다.
- After Throwing Advice (예외 발생 이후 어드바이스) : 특정 메서드에서 예외가 발생한 후에 실행됩니다. 예외 처리나 오류 로깅과 같은 작업을 수행할 수 있습니다.
- After Advice (이후 어드바이스) : 메서드가 실행된 후(예외가 발생하던 정상 반환하던) 실행됩니다. 예를 들어, 메서드 실행 후 정리 작업을 수행하는 데 사용될 수 있습니다.
- Around Advice (주위 어드바이스) : 메서드 실행 전과 후, 또는 예외가 발생한 경우 등 모든 시점에서 실행됩니다. 가장 유연한 형태의 어드바이스로, 메서드 실행 전후에 로깅, 트랜잭션 처리, 예외 처리 등을 일괄적으로 수행할 수 있습니다.
코드 구현
package com.spring.biz.common;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import com.spring.biz.member.MemberVO;
@Service
@Aspect
public class LogAdvice {
@Before("PointCutCommon.aPointcut()")
public void beforeLog() {
System.out.println("횡단관심: 비즈니스 메서드 수행 전에 호출됨");
}
@After("PointCutCommon.aPointcut()")
public void afterLog() {
System.out.println("[횡단관심]");
System.out.println(" 비즈니스 메서드 수행 후에 호출됨");
System.out.println();
}
@AfterReturning(pointcut="PointCutCommon.bPointcut()", returning="returnObj")
public void afterReturningLog(JoinPoint jp, Object returnObj) {
String methodName=jp.getSignature().getName();
System.out.println("횡단관심 : "+methodName+"의 반환 이후의 로그");
if(returnObj instanceof MemberVO) {
MemberVO mVO=(MemberVO)returnObj;
if(mVO.getRole().equals("ADMIN")) {
System.out.println("[관리자 입장]");
}
else {
System.out.println("[사용자 입장]");
}
}
else {
System.out.println("[데이터 열람]");
}
}
@AfterThrowing(pointcut="PointCutCommon.aPointcut()", throwing="exceptObj")
public void afterThrowingPrintLog(JoinPoint jp, Exception exceptObj) {
String methodName = jp.getSignature().getName();
System.out.println("횡단 관심 : "+methodName+"에서 예외가 발생해서 출력되는 로그");
System.out.println("예외 메세지 : "+exceptObj.getMessage());
}
@Around("PointCutCommon.aPointcut()")
public Object aroundPrintLog(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around 로그 전");
StopWatch sw=new StopWatch();
sw.start();
Object obj=pjp.proceed();
sw.stop();
String methodName=pjp.getSignature().getName();
System.out.println(methodName+" 메서드를 수행하는데에 소요한 시간은 "+sw.getTotalTimeMillis()+"초입니다.");
System.out.println("around 로그 후");
return obj;
}
}
LogAdvice.java
LogAdvice를 보면 해당 로그 메서드들이 어느 객체 혹은 비즈니스 메서드가 인자로 들어올지 모르는 상태입니다. 따라서 객체의 경우 최상위 객체인 Object를, 비즈니스 메서드의 경우 JoinPoint를 인자로 선언합니다.
바인딩 변수
바인딩 변수는 AOP에서 어드바이스 메서드에 전달되는 인자 또는 파라미터를 가리키는 용어입니다. AOP에서 어드바이스는 특정한 시점에서 실행되는 코드 블록이며, 이 코드 블록은 횡단 관심사를 다루기 위한 목적으로 사용됩니다. 바인딩 변수를 사용하면 어드바이스 메서드가 실행 시점의 메서드 동작 및 결과를 감시하고 조작할 수 있어, AOP를 통해 횡단 관심사를 구현하는 데 유용합니다.
GitHub
https://github.com/Qkrwnsgus0522/Spring