[Spring] @Aspect 프록시

서규범·2022년 11월 20일
0

자동 프록시 생성기

  • 스프링에서 제공하는 AnnotationAwareAspectJAutoProxyCreator는 Advisor를 자동으로 찾아와서 필요한 곳에 프록시를 생성하고 적용

  • 또한 @Aspect 를 찾아서 이것을 Advisor 로 변환해서 저장

  • 어드바이저를 기반으로 프록시를 생성


Advisor란?

  • 포인트컷 + 어드바이스

자동 프록시 생성기의 작동 과정

  1. 생성: 스프링 빈 대상이 되는 객체를 생성 ( @Bean , 컴포넌트 스캔 모두 포함)

  2. 전달: 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.

  1. Advisor 빈 조회: 스프링 컨테이너에서 Advisor 빈을 모두 조회한다.

  2. @Aspect Advisor 조회: @Aspect 어드바이저 빌더 내부에 저장된 Advisor 를 모두 조회한다.

  3. 프록시 적용 대상 체크: 앞서 3, 4에서 조회한 Advisor 에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단. 이때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메서드를 포인트컷에 하나하나 모두 매칭. 그래서 조건이 하나라도 만족하면 프록시 적용 대상이 됨. 예를 들어서 메서드 하나만 포인트컷 조건에 만족해도 프록시 적용 대상이 됨

  4. 프록시 생성: 프록시 적용 대상이면 프록시를 생성하고 프록시를 반환. 그래서 프록시를 스프링 빈으로 등록. 만약 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록

  5. 빈 등록: 반환된 객체는 스프링 빈으로 등록


예제 코드 - AOP 자동 프록시 생성

@Slf4j
@Aspect
public class AspectV1 {
 
	@Around("execution(* hello.aop.order..*(..))")
	public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
 		log.info("[log] {}", joinPoint.getSignature()); //join point 시그니처
		return joinPoint.proceed();
 
	}
}
  • @Around 애노테이션의 값인 execution( hello.aop.order..(..)) 가 포인트컷
  • @Around 애노테이션의 메서드인 doLog 는 어드바이스

예제 코드 - 포인트컷 분리

@Slf4j
@Aspect
public class AspectV2 {
 
	//hello.aop.order 패키지와 하위 패키지
	@Pointcut("execution(* hello.aop.order..*(..))") //pointcut expression
	private void allOrder(){} //pointcut signature
 
	@Around("allOrder()")
	public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
 		log.info("[log] {}", joinPoint.getSignature());
		return joinPoint.proceed();
 
	}
}
  • @Pointcut 에 포인트컷 표현식을 사용
  • 메서드 이름과 파라미터를 합쳐서 포인트컷 시그니처(signature)라 함
  • 메서드의 반환 타입은 void 여야함
  • 코드 내용은 비워둠
  • @Around 어드바이스에서는 포인트컷을 직접 지정해도 되지만, 포인트컷 시그니처를 사용해도 됨
  • 다른 애스팩트에서 참고하려면 public 을 사용해야 함

예제 코드 - 다중 어드바이스

@Slf4j
@Aspect
public class AspectV3 {
 
	//hello.aop.order 패키지와 하위 패키지
	@Pointcut("execution(* hello.aop.order..*(..))")
	public void allOrder(){}
 
	//클래스 이름 패턴이 *Service
	@Pointcut("execution(* *..*Service.*(..))")
	private void allService(){}
    
	@Around("allOrder()")
	public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
 		log.info("[log] {}", joinPoint.getSignature());
		return joinPoint.proceed();
	}
 
	//hello.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service
	@Around("allOrder() && allService()")
	public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
 
	try {
	 log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
	 Object result = joinPoint.proceed();
	 log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
	 return result;
 
	} catch (Exception e) {
 	log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
	throw e;
 
	} finally {
	 log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
 
	}
 
	}
}
  • allOrder() 포인트컷은 hello.aop.order 패키지와 하위 패키지를 대상
  • allService() 포인트컷은 타입 이름 패턴이 *Service 를 대상
  • 포인트컷은 && (AND), || (OR), ! (NOT) 3가지 조합이 가능
  • doLog() 어드바이스는 OrderService , OrderRepository 에 모두 적용
profile
하려 하자

0개의 댓글