인프런 강의 [스프링 핵심 원리 - 고급편] 강의를 기반으로 작성한 글 입니다.
@Aspect
는 관점 지향 프로그래밍(AOP)를 가능하게 하는 AspectJ 프로젝트에서 제공하는 애노테이션이다. 스프링은 이것을 차용해서 프록시를 통한 AOP가 가능하게 한다.
자동 프록시 생성기(AnnotationAwareAspectAutoProxyCreator
)는 Advisor
를 자동으로 찾아와서 필요한 곳에 프록시를 생성하고 적용해준다.
자동으로 적용해 주는 것 외에도, @Aspect
를 찾아서 자동으로 Advisor
로 만들어 준다.
@Aspect
애노테이션이 붙은 스프링 빈을 모두 조회한다.@Aspect
어드바이저 빌더를 통해 @Aspect
애노테이션 정보를 기반으로 어드바이저를 생성한다.@Aspect
어드바이저 빌더 내부에 저장한다.@Aspect 어드바이저 빌더 :
@Aspect
정보를 기반으로Pointcut
,Advice
,Advisor
를 생성하고 보관하는 것을 담당.@Aspect
의 정보를 기반으로 어드바이저를 만들고, `@Aspect 어드바이저 빌더 내부 저장소에 캐시함. 이미 만들어져 있는 경우 캐시에 저장된 어드바이저를 반환
@Aspect
public class LogTraceAspect {
private final LogTrace logTrace;
public LogTraceAspect(LogTrace logTrace) {
this.logTrace = logTrace;
}
@Around("execution(* hello.proxy.app..*(..))") // pointcut
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
// advice 로직
TraceStatus status = null;
try{
String message = joinPoint.getSignature().toShortString();
status = logTrace.begin(message);
// 로직 호출
Object result = joinPoint.proceed();
logTrace.end(status);
return result;
} catch (Exception e){
logTrace.exception(status, e);
throw e;
}
}
}
프록시에 중요한 것은 Pointcut
과 Advice
이다.
Aspect 애노테이션을 통해서 AspectJ를 적용하자.
그리고 Pointcut
은 execute
메소드 상단에 @Around
애노테이션으로 사용이 가능하다.
포인트 컷을 자세히 보면 * hello.proxy.app..*(..))
인데, hello.proxy.app
패키지 안에 있는 모든 클래스와 메소드에 적용된다.
모든 프로그램에는 핵심 기능과 부가 기능이 있다.
클래스도 핵심 기능과 부가 기능으로 나눌 수 있다.
주문 로직 클래스
- 핵심 기능 : 주문 하는 로직 (주문 → DB 저장..)
- 부가 기능 : 로그 출력하기
이 부가 기능인 로그 출력하기 메소드가 여러 클래스에서 필요할 수 있다.
Controller - Service - Repository를 예시로 보면, 이 모든 클래스들에서 동일한 메소드가 필요할 수 있다.
이것을 횡단 관심사 라고 한다.
근데 동일한 메소드를 모든 클래스에 생성하게 되면 불필요한 코드 생성과 수정에 문제가 생긴다.
문제들
- 부가 기능을 적용할 때 아주 많은 반복이 필요하다.
- 부가 기능이 여러 곳에 퍼져서 중복 코드를 만들어낸다.
- 부가 기능을 변경할 때 중복 때문에 많은 수정이 필요하다.
- 부가 기능의 적용 대상을 변경할 때 많은 수정이 필요하다.
부가 기능을 핵심 기능에서 분리하고 한곳에서 관리.
부가 기능을 어디에 적용할지 선택하는 기능도 만들었다.
Aspect
를 통해서 하나의 모듈로 만들었다.
AOP
Asepct-Oriented Programming
: Aspect를 사용한 프로그래밍 방식을 관점 지향 프로그래밍
AspectJ : Aspect를 지원하기 위한 프레임워크
AOP를 사용해서 부가 기능 로직을 추가하는 방법
1. 컴파일 시점
2. 클래스 로딩 시점
3. 런타임 시점 (프록시)
.java
소스 코드를 컴파일러를 사용해서 .class
로 만드는 시점에 부가 기능 로직을 추가할 수 있다.
AspectJ 컴파일러가 Aspect를 확인해서 해당 클래스가 적용 대상인지 확인하고, 적용 대상인 경우에 부가 기능 로직을 적용함.
*Weaving
이라고도 한다
자바를 실행하면 자바 언어는 '.class' 파일을 JVM 내부의 클래스 로더에 보관한다.
이때 중간에서 '.class'파일을 조작한 다음 JVM에 올릴 수 있다. Java Imstrumentation을 통해서 가능
단점
자바를 실행할 때 특별한 옵션 (java -javaagent
)을 통해 클래스 로더 조작기를 지정해야한다.
굉장히 번거롭고 운영하기 어려움.
컴파일도 끝나고, 클래스 로더에 다 올라가서 자바가 실행되고 난 뒤를 런타임 시점이라고 한다.
이때는, 메소드를 수정할 수 없기 때문에 스프링 컨테이너의 도움을 받아서 프록시와 DI, 빈 포스트 프로세서를 사용해서 적용해야한다.
이것이 프로시 방식의 AOP이다.
AOP 적용 위치
스프링 AOP | AspectJ AOP |
---|---|
프록시 방식만 사용 가능 | 컴파일 시점, 클래스 로딩 시점, 프록시 방식 |
단순하게 사용가능 | 굉장히 복잡함 |
스프링 어플리케이션 | 특별한 컴파일러, AspectJ 문법, 자바 실행 옵션 |