Spring AOP(Aspect-Oriented Programming)
Spring AOP에서 AOP는 Aspect-Oriented Programming으로 관점 지향 프로그래밍을 의미한다. 관점 지향 프로그래밍이란 관심사를 적절히 나누자는 것인데 AOP에서는 이를 공통 관심 사항(cross-cutting concern)과 핵심 관심 사항(core concern)으로 분리한다. AOP라는 말에서 OOP와 대치될 것 같다는 느낌을 받을 수 있지만, 전혀 그렇지 않고 AOP는 오히려 더욱더 OOP의 관점으로 프로그래밍 할 수 있도록 도와준다.
Spring AOP 예제 - 메소드의 호출 시간 측정
AOP에 대해 공부하면서 많은 자료를 찾아봤는데 거의 모든 자료에서 AOP의 예제로 메소드의 호출 시간을 측정하는 것을 설명하고 있다. 가장 이해하기 쉽고 정확한 예제이지 않을까 생각하고 해당 포스트도 시간측정을 주제로 AOP 개념을 기록하고자 한다.
아래와 같이 프로젝트의 모든 메서드의 호출 시간을 측정하고 싶다고 해보자. 모든 메서드의 시작과 끝에 시간 측정 로직을 추가해야할 것이다. 하지만, 만약 1억개의 메서드가 있거나 측정 기준을 수정해야하는 상황이 발생한다면 사실상 업무를 할 수 없을 것이다. 아래와 같은 문제점들을 해결하고자 AOP의 개념이 등장하였고, AOP개념을 통해 모든 메서드에 적용해야하는 공통관심사항과 핵심 비즈니스로직을 분리해 개발 효율과 유지보수성을 높일 수 있다.
long start = System.currentTimeMillis();
try {
...
} finally {
long finish = System.currenTimeMillis();
long timeMs = finish - start;
System.out.println("timeMs = " + timeMs + "ms");
}
[ 문제점 ]
1. 서비스 메서드에서 시간을 측정하는 기능은 핵심 관심 사항이 아니다.
2. 시간을 측정하는 로직은 공통 관심 사항이다. (공통의 Aspect)
3. 시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수가 어렵다.
4. 시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어렵다.
5. 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 한다.
위 코드에서 AOP를 이용해 공통관심사항을 핵심관심사항과 분리해보자. 분리한 공통관심사항에 맞는 클래스를 생성하고 aspectj의 @Aspect를 이용해 해당 클래스가 AOP 클래스임을 알려준다. 또한, SpringAOP는 Spring 컨테이너가 관리하는 Bean에만 적용할 수 있기때문에 @Component를 선언해준다. @Around 어노테이션으로 AOP를 적용할 범위를 설정해주고 범위 내의 메서드가 실행되면 아래와 같이 메서드의 시작, 종료시점에 맞춰 AOP가 동작한다.
//Component Scan을 이용하기보다는 Bean으로 등록해 App에서 TimeTraceAop가 쓰인다는 것을 알려주는것이 좋다.
//물론 ComponentScan을 써도 된다.
@Aspect
@Component
public class TimeTraceAop {
//@Around: 기능 수행의 전과 후(@Before + @After)
@Around("execution(* hello.hellopspring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
//다음 메서드를 가져오는 부분
return joinPoint.proceed();
} finally {
long finish = System.currenTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
AOP에는 위 코드에서 @Around와 같이 AOP기능을 언제 실행지킬지에 대해 정의하는 어드바이스와 AOP기능을 어디에 붙힐지에 대해 정의하는 포인트컷이라는 개념을 정의하고 있다. 어드바이스의 종류로는 @Around 뿐만 아니라 @Before, @After, @AfterReturning 등 다양한 종류가 있으니 알아보면 좋을 것 같고 포인트컷은 아래와 같이 사용하면 된다.
@Component
@Aspect
public class TimeTraceAop {
@Pointcut("execution(* com.tistory.thinkinpotato.A..*(..))")
public void A() {}
@Pointcut("execution(* com.tistory.thinkinpotato.B..*(..))")
public void B() {}
@Pointcut("execution(* com.tistory.thinkinpotato.C..*(..))")
public void C() {}
@Around("A() || B() || C()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
https://docs.spring.io/spring-framework/reference/core/aop.html