8. 관점 지향 프로그래밍 (AOP)
- AOP
- Spring AOP 활성화
- AOP 주요 용어 정리
- Spring AOP 문법 - Point Cut
- Pointcut - Designator(지시자) 옵션
- Pointcut - 표현식 예제
- Spring AOP 문법 - Advice
- Advice - 사용
- Advice - JoinPoint 활용
8. 관점 지향 프로그래밍 (AOP)
AOP
- 프로그램 구조를 다른 방식으로 생각하게 함으로써 OOP를 보완.
- OOP에서 모듈화의 핵심 단위는 Class 이지만, AOP에서 모듈화의 핵심 단위는 Aspect.
- 관점(Aspect)는 다양한 타입과 객체에 걸친 관심(Concern)을 모듈화 할 수 있게 한다.
- 관심사를 분리하여, 관점별로 각각의 기능을 모듈화.
- 설정을 추가하여 Weaving함.
(Weaving: Advice를 Target Object에 적용하는 과정)


Spring AOP 활성화
- pom.xml에 spring-boot-starter-app 의존성 추가.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Spring aop 예시)
@Aspect
@Component
public class GreetingAop {
@Pointcut("execution(* org.example.greeting..sayHello())")
public void cut() {
}
@Around("cut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
System.out.println("===== StopWatch start =====");
stopWatch.start();
Object object = joinPoint.proceed();
stopWatch.stop();
System.out.println("===== StopWatch stop =====");
System.out.println(stopWatch.prettyPrint());
return object;
}
}
AOP 주요 용어 정리
더보기


Aspect
- 관심사를 모듈화한 클래스.
- AOP의 기본 구성 요소로서, 특정 조건에서 특정 작업을 수행하도록 구성된 모듈.
- 하나 이상의 @Pointcut과 Advice(@Before, @After, @Around...)의 조합으로 만들어지는 AOP의 기본 모듈.
- Spring에서는 @Aspect annotation을 포함하는 클래스.
Join Point (어디에)
- Aspect가 적용될 수 있는 지점 (메소드 실행, Exception 처리 ... 등)
- Pointcut의 후보.
- Spring AOP에서는 메소드 실행만 대상이다.
Advice (무엇을)
- 특정 Join Point에 Aspect에 의해 취해질 조치.
- Before, After, Around, AfterReturning, AfterThrowing ... 등 다양한 타입이 있다.
Pointcut
- Advice를 적용할 Join Point를 선별하는 작업.
- Advice는 Pointcut 표현식과 연결되고, Pointcut이 매치한 Join Point에서 실행됨.
Target object
- Advice가 적용되는 대상 객체
Weaving
- Advice를 Target Object에 적용하는 과정.
- 컴파일 타임, 로드 타임, 런타임 등에 수행될 수 있다.
AOP Proxy (Spring)
- AOP Framework가 생성하는 객체.
- Target Object를 감싸며, Advice 정의에 따라 동작.

Advisor (Spring)
- Pointcut과 Advice를 하나씩 갖고 있는 객체.
- 개발자가 Pointcut과 Advice를 설정하면, 이를 결합하여 Advisor 객체를 생성.
Spring AOP
- AOP 개념을 Spring Bean에 적용하기 위한 것.
- Spring Bean 대상이므로, ApplicationContext가 처리함.
- Runtime weaving.
- Spring에서는 AOP 구현을 위해 Proxy를 사용한다.
- Spring에서는 Bean을 Proxy로 감싸서 앞 뒤에 로직을 넣는 기능을 제공한다.
- Spring에서는 Aspect, Join point, Advice 등의 어노테이션으로 이를 제어한다.

Spring AOP 문법 - Point Cut
- Target의 여러 Join point 중에서 Advice를 적용할 대상을 재정하는 키워드.
- Spring AOP는 Spring Bean의 '메소드 실행' join point만 지원한다.
- Point Cut 선언은 expression(조건)과 signature(이름)로 구성된다.
@Pointcut(
"execution(" // Pointcut Designator(지시자): 어떤 타입에 적용할지를 결정
+ "[접근제한자 패턴] " // public: 생략 가능
+ "리턴타입 패턴" // *: 와일드 타입 사용 가능
+ "[패키지명, 클래스 경로 패턴]" // '..', '*', '+' 와일드 카드 사용 가능
+ "메소드명 패턴(파라미터 타입 패턴|..)" // .greet(User, String)
+ "[throws 예외 타입 패턴]" // 생략 가능
+")"
)
@Pointcut("execution(* org.example.greeting.Greeting.sayHello())") // the pointcut expression
public void cut() { } // the pointcut signature
와일드 카드
더보기
1. * (별표)
하나의 클래스, 메소드, 패키지 이름 등을 대신하는 와일드 카드.
ex) execution(* get*(..)): get으로 시작하는 모든 메소드에 매칭.
2. .. (점 두 개)
메소드 파라미터 인자의 수나, 패키지 경로에서 하나 이상의 하위 패키지를 포함할 수 있음을 의미.
ex) execution(* com.example..*(..)): com.example 패키지와 모든 하위 패키지의 모든 메소드를 매칭.
*() vs *(..)
첫 번째는 hello()와 같은 메소드 파라미터가 없는 메소드들을 의미.
두 번째는 hello(), hello(String a, int b)처럼 메소드 파라미터에 상관없이 모든 메소드를 의미.
3. + (플러스)
클래스 이름 뒤에 사용되며, 지정된 클래스 또는 인터페이스를 구현하거나 확장하는 모든 클래스와 매칭.
ex) within(com.example.service.Service+): Service interface를 구현하는 모든 클래스에 매칭.
Pointcut - Designator(지시자) 옵션
- execution
- 가장 일반적으로 사용되는 지시자로, 메소드 실행을 가로채는 데 사용
- within
- 특정 타입(클래스나 인터페이스) 내의 모든 조인 포인트를 매칭
- 주로 특정 패키지나 클래스 내의 메소드를 대상으로 할 때 사용
- this
- 주어진 타입을 구현한 스프링 AOP Proxy 객체에 매칭
- target
- this와 유사하지만, 주어진 타입을 구현한 타겟 객체에 매칭
- args
- 메소드가 받는 인자의 타입을 기반으로 조인 포인트를 매칭
- @taget
- 인자가 아닌, 객체의 클래스에 붙은 어노테이션을 기준으로 조인 포인트를 매칭
- @args
- 메소드의 인자에 적용된 어노테이션을 기준으로 조인 포인트를 매칭
- @within
- 클래스 레벨에 적용된 어노테이션을 기준으로 조인 포인트를 매칭
- @annotation
- 메소드 레벨에 적용된 어노테이션을 기준으로 조인 포인트를 매칭
- bean
- 특정 빈 이름에 해당하는 빈에 대해서만 조인 포인트를 매칭
Pointcut - 조합
- 포인트컷 표현식은 &&, ||, ! 으로 조합할 수 있다.
// anyPublicOperation 포인트컷은 모든 public 메소드 실행에 매칭 된다.
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
// inTrading 포인트컷은 com.xyz.myapp.trading 패키지 내의 메소드 실행에 매칭
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {}
// tradingOperation 포인트컷은 com.xyz.myapp.trading 패키지 내의 퍼블릭 메소드 실행에 매칭
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
Pointcut - 표현식 예제
더보기
Spring AOP는 주로 execution Pointcut 지정자를 사용한다.
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
모든 public 메소드
execution(public * *(..))
get~ 으로 시작하는 모든 메소드
execution(* get*(..))
org.example 패키지에 있는 모든 메소드
execution(* org.example..*(..))
org.example.greeting.Greeting 인터페이스에 정의된 모든 메소드
execution(* org.example.greeting.Greeting.*(..))
org.example.greeting 패키지의 모든 메소드 실행
within(org.example.greeting.*)
SpecialGreeting Proxy 구현체의 메소드 실행
this(org.example.greeting.SpecialGreeting)
SpecialGreeting 인터페이스의 구현 객체의 메소드 실행
target(org.example.greeting.SpecialGreeting)
런타임에 SpecialHelloTarget 타입의 단일 파라미터가 전달되는 메소드 실행
args(org.example.greeting.SpecialHelloTarget)
@Transactional 어노테이션을 가진 모든 타겟 객체의 메소드 실행
@target(org.springframework.transaction.annotation.Transactional)
Spring AOP 문법 - Advice
- Advice는 Pointcut과 관련하여 메소드 실행 전, 후, 전/후 를 결정하기 위해 사용.
| Advice | 형태설명 |
| Before | Join Point 앞에서 실행할 Advice |
| After | Join Point 뒤에서 실행할 Advice |
| AfterReturning | Join Point가 완전히 정상 종료한다음 실행하는 Advice |
| Around | Join Point 앞과 뒤에서 실행되는 Advice |
| AfterThrowing | Join Point에서 예외가 발생했을때 실행되는 Advice |

Advice - 사용
@Aspect
@Component
public class GreetingAop {
//TODO-1 실행 순서를 확인한다.
// around 시작 -> before -> afterReturn(afterThrowing) -> after -> around 종료
@Pointcut("execution(* org.example.greeting..sayHello())")
public void cut() {
}
// Advice들
@Before("cut()")
public void before() throws Throwable {
System.out.println("before");
}
@AfterReturning("cut()")
public void afterReturning() throws Throwable {
System.out.println("afterReturning");
}
@AfterThrowing("cut()")
public void afterThrowing() throws Throwable {
System.out.println("afterThrowing");
}
@After("cut()")
public void after() {
System.out.println("after");
}
@Around("cut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around1");
Object object = joinPoint.proceed();
System.out.println("around2");
return object;
}
}
Advice - JoinPoint 활용
- 모든 Advice 메소드는 첫 번째 인자로 JoinPoint를 받을 수 있다.
- Around Advice는 JoinPoint의 subClass인 ProceedingJoinPoint를 반드시 사용해야 한다.
JoinPoint 의 메소드
- getArgs() : 타겟 메소드의 인자
- getThis() : 프록시 객체
- getTarget() : 타겟 객체
- getSignature() : 타겟 객체의 메소드 시그니쳐
- toString() : 타겟 객체의 메소드 정보
@Slf4j
@Component
@Aspect
public class AccountAop {
@Pointcut("execution(* com.example.demo.account.service.AuthenticationService.login(..)) " +
"|| execution(* com.example.demo.account.service.AuthenticationService.logout(..))")
public void accountCut() {}
@Around("accountCut()")
public Object beforeLogin(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("{}({})", joinPoint.getSignature().getName(),Arrays.toString(joinPoint.getArgs()));
return joinPoint.proceed();
}
}
* 주의: Spring aop는 Proxy 기반으로 동작하기 때문에, 외부에서 받은 호출만 aop로 감쌀 수 있다.
출처: https://nhnacademy.dooray.com/share/pages/ABFgz8KgRv62A85x9KClUw
Spring Boot Core
nhnacademy.dooray.com
'Spring' 카테고리의 다른 글
| [Spring Boot] JDK Proxy vs CGLIB Proxy (0) | 2024.11.24 |
|---|---|
| [Spring Boot] 서비스 추상화 (Portable Service Abstraction) (1) | 2024.11.09 |
| [Spring Boot] @ConfigurationProperties 활성화시 @Profile 무시 에러 (0) | 2024.11.08 |
| [Spring Boot] 01. Spring Boot Core (2) - 자동 구성과 외부 구성 (0) | 2024.11.06 |
| [Spring Boot] Lombok (0) | 2024.11.06 |