📝 Spring AOP 개념정리 및 어노테이션 별 Logger테스트
#230512
[board]-[common]-[aop]-TestAspect
[board]-[common]-[aop]-CommonPointcut
[board]-[common]-[aop]-BeforeAspect
[board]-[common]-[aop]-AfterAspect
[board]-[common]-[aop]-AfterReturningAspect
[board]-[common]-[aop]-AfterThrowingAspect
[board]-[common]-[aop]-AroundAspect
런타임시 필요한 코드를 알아서 참여시킬 수 있도록 bean 등록
공통 관심사(특정 흐름 사이에 끼여서 수행할 코드)가 작성된 클래스임을 명시
해당 어노테이션이 작성된 클래스에서는
advice(끼여들어서 수행할 메서드), pointcut(advice가 끼여들어서 수행될 부분) 이 작성되어 있어야한다.
💻 joinpoint : advice가 적용될 수 있는 지점
💻 pointcut : joinpoint 중에서 실제로 advice를 적용할 지점 선택
@Pointcut("execution([접근제한자] 반환형 패키지+클래스명.메서드명([매개변수]))")
* : 모든 | 아무 값
.. : 하위 | 아래 (하위 패키지) | 0개 이상의 매개변수
package edu.kh.comm.common.aop;
import org.aspectj.lang.annotation.Pointcut;
public class CommonPointcut {
// 회원 서비스용 Pointcut
@Pointcut("execution(* edu.kh.comm.member..*Impl.*(..))")
public void memberPointcut() {}
// 모든 ServiceImpl 클래스용 Pointcut
@Pointcut("execution(* edu.kh.comm..*Impl.*(..))")
public void implPointcut() {}
}
advice가 적용되는 Target Object (ServiceImpl)의
정보, 전달되는 매개변수, 메서드, 반환값, 예외 등 얻을 수 있는 메서드를 제공
❗주의사항 : JoinPoint 인터페이스는 항상 첫 번째 매개변수로 작성되어야 한다!
@Before("CommonPointcut.implPointcut()")
public void serviceStart(JoinPoint jp) {
//jp.getTarget() : aop가 적용된 객체 (각종 ServiceImpl)
String className = jp.getTarget().getClass().getSimpleName();
// jp.getSignature() : 수행되는 메서드 정보
String methodName = jp.getSignature().getName();
// jp.getArgs() : 메서드 호출 시 전달된 매개변수
String param = Arrays.toString(jp.getArgs() );
str += "Start : " + className + " - " + methodName + "\n";
// Start : MemberServiceImpl - login
str += "Parameter : " + param + "\n";
타겟 객체의 메소드가 실행 되기 전에 호출되는 어드바이스
타겟 객체의 메소드 호출 전과 후에 실행 될 코드를 구현할 어드바이스
기존 @After + 반환값 얻어오기 기능
css의 z-index 와 같은거
advice 실행 순서, 클 수록 먼저 시작
@Component
@Aspect
@Order(5)
public class AfterReturningAspect {
private Logger logger = LoggerFactory.getLogger(AfterReturningAspect.class);
@AfterReturning(pointcut = "CommonPointcut.implPointcut()" , returning = "returnObj")
public void serviceReturnValue(JoinPoint jp, Object returnObj) {
logger.info("Return Value : " + returnObj);
}
}
기존 @After + 던져지는 예외 얻어오기 기능
@Component
@Aspect
public class AfterThrowingAspect {
private Logger logger = LoggerFactory.getLogger(AfterThrowingAspect.class);
@AfterThrowing(pointcut = "CommonPointcut.implPointcut()" , throwing = "exceptionObj")
public void serviceReturnValue(JoinPoint jp, Exception exceptionObj) {
String str = "<<exception>> : " + exceptionObj.getStackTrace()[0];
str += " - " + exceptionObj.getMessage() + "\n";
logger.error(str);
}
}
@Component
@Aspect
@Order(4)
public class AroundAspect {
private Logger logger = LoggerFactory.getLogger(AroundAspect.class);
@Around("CommonPointcut.implPointcut()")
public Object runningTime(ProceedingJoinPoint jp) throws Throwable {
// proceed() 메소드 호출 전 : @Before advice 작성
// proceed() 메소드 호출 후 : @After advice 작성
// 메소드 마지막에 proceed()의 반환값을 리턴해야함
// System.currentTimeMillis() :
// 1970/01/01 오전 9시 (한국 OS 기준) 부터
// 지금까지 지난 시간을 ms 단위로 나타낸 값
long startMs = System.currentTimeMillis(); // before(전처리)
Object obj = jp.proceed(); // 전/후 처리를 나누는 기준
long endMs = System.currentTimeMillis(); // after (후처리)
logger.info("Running Time : " + (endMs - startMs) + "ms");
return obj;
// proceed()반환값 리턴해주기
}
}