Spring - AOP(Aspect-Oriented Programming)
목차
- Aspect
- Advice
- Pointcut
- JoinPoint
배운 내용
애스펙트(Aspect)
- 부가 기능을 정의한 코드인
어드바이스(Advice)
와 어드바이스를 어디에 적용할지 결정하는 포인트컷(PointCut)
을 합쳐 하나의 모듈로 만든것
- 여러 객체에 공통으로 적용되는 기능
- AOP는 기존에 사용하던 OOP를 대체하기 위한 것이 아닌 횡단 관심사를 깔끔하게 처리하기 위해 OOP의 부족한 부분을 보조하는 목적으로 개발됨
핵심 기능(Core Concerns)
부가 기능(CROSS-CUTTING CONCERNS)
**조인 포인트(join point)**
- 객체 인스턴스화, 메소드 호출 같은 애플리케이션 실행 흐름에서의 특정 포인트를 의미 AOP를 적용할 수 있는 모든 지점, 포인트컷 후보
- 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한
**어드바이스(Advice)**
- 조인포인트에서 수행되는 횡단관심에 해당되는 공통 기능의 코드
- Aspect를 언제 핵심 코드에 적용할지를 정의
**포인트컷(Pointcut)**
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- AspectJ 표현식을 사용해서 지정
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능
**위빙(Weaving)**
- 포인트컷으로 결정한 타겟의 조인 포인트에 어드바이스를 적용하는 것
**AOP 프록시(proxy)**
- AOP 기능을 구현하기 위해 만든 프록시 객체
- JDK 동적 프록시 or CGLIB 프록시
**타겟 (Target)**
- Adivce를 받는 객체이고 포인트컷으로 결정된다.
**어드바이저(Advisor)**
- 하나의 어드바이스와 하나의 포인트 컷으로 구성
- 스프링 AOP에서만 사용되는 특별한 용어
**Advice 순서**
- 어드바이스는 기본적으로 순서를 보장하지 않는다.
- 순서를 지정하고 싶으면
@Aspect
적용 단위로@Order
애너테이션을 적용해야 한다.
- 어드바이스 단위가 아니라 클래스 단위로 적용가능
- 하나의 애스펙트에 여러 어드바이스가 존재하면 순서를 보장받을 수 없기 때문에 별도의 클래스로 애스펙트를 분리해야 한다.
**Advice 종류**
**Before**
- 조인 포인트 실행 이전에 실행
- Before Advice 구현한 메서드는 일반적으로 리턴타입이 void 이다.
**After returning**
- 조인 포인트가 정상 완료 후 실행
- 메서드가 예외 없이 실행된 이후에 공통 기능을 실행
**After throwing**
**After (finally)**
- 조인 포인트의 동작(정상 또는 예외)과는 상관없이 실행
- 메서드 실행 후 공통 기능을 실행
- 일반적으로 리소스를 해제하는데 사용
**Around**
- 메서드 실행 전 & 후, 예외 발생 시점에 공통 기능을 실행
- 조인 포인트 실행 여부 선택, 반환 값 변환, 예외 변환 등이 가능
- 어드바이스의 첫 번째 파라미터는 ProceedingJoinPoint를 사용해야 한다.
- proceed()를 통해 대상을 실행
**Pointcut 표현식**
**포인트컷 지시자**
- 포인트컷 표현식은 execution 같은 포인트컷 지시자로 시작
**Pointcut 표현식 결합**
- 포인트컷 표현식은
&&, ||, !
를 사용하여 결합 가능
anyPublicOperation
은 메서드 실행 조인 포인트가 공용 메서드의 실행을 나타내는 경우 일치합니다.
in Trading
메서드 실행이 거래 모듈에 있는 경우에 일치합니다.
tradingOperation
은 메서드 실행이 거래 모듈의 공개 메서드를 나타내는 경우 일치
**일반적인 pointcut 표현식**
- 모든 공개 메서드 실행
execution(public * *(..))
set
다음 이름으로 시작하는 모든 메서드 실행
AccountService
인터페이스에 의해 정의된 모든 메소드의 실행
execution(* com.xyz.service.AccountService.*(..))
service
패키지에 정의된 메서드 실행
execution(* com.xyz.service.*.*(..))
- 서비스 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
execution(* com.xyz.service..*.*(..))
- 서비스 패키지 내의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
within(com.xyz.service.*)
- 서비스 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
within(com.xyz.service..*)
AccountService
프록시가 인터페이스를 구현하는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
this(com.xyz.service.AccountService)
AccountService
대상 객체가 인터페이스를 구현하는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
target(com.xyz.service.AccountService)
- 단일 매개변수를 사용하고 런타임에 전달된 인수가
Serializable
과 같은 모든 조인 포인트 (Spring AOP에서만 메소드 실행)
args(java.io.Serializable)
- 대상 객체에
@Transactional
애너테이션이 있는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
@target(org.springframework.transaction.annotation.Transactional)
- 실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트 (Spring AOP에서만 메서드 실행)
@annotation(org.springframework.transaction.annotation.Transactional)
- 단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트(Spring AOP에서만 메서드 실행)
@args(com.xyz.security.Classified)
tradeService
라는 이름을 가진 스프링 빈의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- 와일드 표현식
Service
라는 이름을 가진 스프링 빈의 모든 조인 포인트
JoinPoint
- AOP 를 수행하는 메소드는 이 JoinPoint 인스턴스를 인자로 받게 된다.
- JoinPoint 인스턴스에서 조인 포인트 지점의 정보를 얻어내야 한다.
- 프록시는 메서드 오버라이딩 개념으로 동작
- 생성자나 static 메서드, 필드 값 접근에는 프록시 개념이 적용불가
- 스프링 AOP는 스프링 빈에만 AOP를 적용가능
**JoinPoint 인터페이스의 주요 기능**
- JoinPoint.getArgs() : JoinPoint에 전달된 인자를 배열로 반환합니다.
- JoinPoint.getThis() : AOP 프록시 객체를 반환합니다.
- JoinPoint.getTarget() : AOP가 적용된 대상 객체를 반환합니다.
- 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체를 반환합니다.
- JoinPoint.getSignature() : 조언되는 메서드에 대한 설명을 반환합니다.
- 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체를 반환
- proceed() : 다음 어드바이스나 타켓을 호출
**@AspectJ 지원**
- @AspectJ는 애너테이션이 있는 일반 Java 클래스로 관점을 선언하는 스타일
활성화
- 자동 프록시 빈에 대한 Spring 지원을 활성화해야한다.
@Configuration
으로 @AspectJ 지원을 활성화하려면 @EnableAspectJAutoProxy
애너테이션을 추가
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
선언
@AspectJ
지원이 활성화되면 @AspectJ 관점
(@Aspect 애너테이션이 있음)이 있는 클래스로 애플리케이션 컨텍스트에 정의된 모든 빈이 Spring에서 자동으로 감지되고 Spring AOP를 구성하는 데 사용된다.
AopUtils.isAopProxy()
를 사용하면 AOP 프록시가 적용 되었는지 확인가능