이 중, 스프링이 제공하는 AOP 방식은 프록시를 이용한 세 번째 방식
AOP 적용 전 의존관계
AOP 적용 후 의존관계
실제 Proxy가 주입되는지 콘솔에 출력해서 확인하기
@Aspect//aop 적용
@Component // 컴포넌트 스캔을 사용해도 되지만 springConfig 에 빈설정정보를 통해 빈등록하는 것이 한눈에 aop정보를 이용해 aop를 하는 구나 알 수 있다.
public class TImeTraceAop {
//@Around("execution(* hello 2.hello..*(..)")
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("Finish:" + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
Aspect: 여러 객체에 공통으로 적용되는 기능으로
@Aspect 어노테이션을 이용하여 공통 기능을 제공하는 클래스 설정
언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의하는데 ,
Advice
는 애플리케이션의 비즈니스 로직과는 별개로, 재사용 가능한 모듈화된 코드조각이다. Advice를 이용하면 애플리케이션의 비즈니스 로직과는 독립적으로, 여러 지점에서 재사용 가능한 로직을 작성할 수 있습니다.
Before Advice: 대상 메서드가 실행되기 전에 실행됩니다.
After Returning Advice: 대상 메서드가 정상적으로 실행되고 반환값이 있는 경우에 실행됩니다.
After Throwing Advice: 대상 메서드가 예외를 던지는 경우에 실행됩니다.
After Finally Advice: 대상 메서드가 실행된 후 항상 실행됩니다.
⭐️ Around Advice: 대상 메서드의 실행 전과 후 모두 실행됩니다. 대상 메서드를 직접 호출하는 것과 비슷하게 작동하며, 대상 메서드를 실행하는 ProceedingJoinPoint 객체를 파라미터로 전달받아 직접 실행 결정을 할 수 있다. Advice가 대상 메서드의 실행을 제어할 수 있다.
//Around Advice 적용
@Around("execution(* hello2.hello..*(..)")
//() 에 타겟팅된 위치에 있는 대상 메서드의 실행 전과 후에 모두 실행됨
@Pointcut("execution(public * cahp07..*(..))")
private void public Target(){
}
스프링 AOP는 public 매서드에만 적용할 수 있기 때문에, 사실상 public만 가능
execution(수식어패턴? 리턴타입패턴 클래스이름패턴?매서드이름패턴(파라미터패턴)) 방식으로 사용
수식어패턴은 생략 가능하며 public, protected등이 올 수 있음
리턴타입패턴은 리턴 타입을 명시
클래스이름패턴, 매서드이름패턴은 클래스 이름 및 매서드 이름을 패턴으로 명시
파라미터패턴은 매칭될 파라미터에서 대해 명시
"..*"은 패키지 내의 모든 하위 패키지를 포함하는 패턴입니다.
예를 들어, "@Pointcut("execution(public * chap07..*(..))")"은 "chap07" 패키지와 "chap07" 패키지의 모든 하위 패키지에 속한 모든 public 메서드를 Pointcut으로 설정합니다.
".."은 0개 이상의 하위 패키지를 의미하며, "*"은 모든 메서드를 의미합니다. 따라서 위의 Pointcut은 "chap07" 패키지와 그 하위 패키지에 속한 모든 public 메서드를 대상으로 합니다.
이 중 널리 사용되는 것은 Around Advice이며,
그 이유는 대상 객체의 매서드를 실행하기 전/후, 익셉션 발생등 다양한 시점에 원하는 기능을 삽입할 수 있기 때문이다.
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {}
//ExeTimeAspect
// 공통 기능을 제공하는 클래스 설정
@Aspect
public class ExeTimeAspect {
// 공통 기능을 적용할 Pointcut 설정
@Pointcut("execution(public * chap07..*(..))")
private void publicTarget() {
}
// publicTarget() 매서드에 정의한 Pointuct에 공통 기능을 적용
@Around("publicTarget()")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
try {
// proceed() 매서드를 사용해서 실제 대상 객체의 매서드를 호출
Object result = joinPoint.proceed();
return result;
} finally {
System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
...
}
}
}
void 리턴 타입
비어있는 매서드
어떤 throws 문도 포함하지 않음
Pointcut을 이용하면 Advice 메소드가 적용될 비즈니스 메소드를 정확하게 필터링 할 수 있다.
지시자(PCD, AspectJ pointcut designators)의 종류
// 패키지의 Controller 라는 이름으로 끝나는 패키지에 모두 적용 (매서드만)
@Pointcut("within(*..*Controller)")
private void cut(){}
의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'
1) Aspect로 사용할 클래스에 @Aspect 어노테이션을 붙임
2) @Pointcut 어노테이션으로 공통 기능을 적용할 Pointcut을 정의
3) 공통 기능을 구현한 매서드에 @Around 어노테이션을 적용
스프링은 Aspect의 적용 대상(Target)이 되는 객체에 대한 Proxy를 만들어 제공한다.
대상 객체(Target)를 사용하는 코드는 대상 객체(Target)를 Proxy를 통해서 간접적으로 접근하게 되며, Proxy는 공통기능(Advice)을 실행한 뒤 대상객체(Target)의 실제 메서드를 호출하거나 또는 대상객체(Target)의 실제 메소드가 호출된 뒤 공통기능(Advice)을 실행한다.
Proxy
Proxy는 타겟을 감싸서 요청을 대신 받아주는 랩핑 클래스이다.
Spring에서는 Proxy를 이용해 객체지향의 5대원칙 중 하나인 OCP를 적용하고 있다.
Open-Close Principal : 개방폐쇄의 원칙
'소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고,
수정에 대해서는 닫혀 있어야 한다.'는 프로그래밍 원칙
참고
김영한 인프런 강의
https://owin2828.github.io/devlog/2019/12/30/spring-7.html#1-3-about-advice
https://velog.io/@gillog/AOP%EA%B4%80%EC%A0%90-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D