Spring AOP(Aspect-Oriented Programming)
관점 지향 프로그래밍 기능
공통 기능을 애플리케이션 코드와 분리, 재사용성을 높이고 코드의 중복을 줄이기 위한 기법
※ AspectJ: AOP의 개념을 Java에 도입한 프레임워크
예를 한번 들어보겠다. 회원가입 기능이 있다고 가정해보자.
얘네들이 핵심 기능 이다.
그럼 이 기능들에 대한 수 많은 부가적인 기능들을 넣게 되면, 그 로직마다 중복되는 코드들을 넣게 된다.
그럼 매우 지저분하고 쓸데없이 반복되게 된다.
"흠.... 이게 무슨 소리냐....🤔" 라고 하실 수 있다.
정의 | 설명 |
---|---|
관점 (Aspect) | 공통 기능을 정의한 모듈 (로깅, 트랜잭션 관리 등) |
조인 포인트 (Join Point) | Aspect가 적용될 수 있는 지점 (메소드 호출 등) |
어드바이스 (Advice) | 조인 포인트에서 수행하는 작업, 어떤 시점에 어떤 기능을 실행할지 정의 |
포인트컷 (Pointcut) | 어드바이스 적용될 조인 포인트를 필터링 |
타겟 (Target) | Aspect가 적용되는 객체 |
프록시 (Proxy) | 프록시 객체를 통해 Aspect를 적용 |
위빙 (Weaving) | AOP 기능이 적용되는 시점 |
어노테이션 | 동작 시기 |
---|---|
@Before | 메소드 실행 전에 동작 |
@After | 메서드 실행 후에 동작 |
@AfterReturning | 메서드가 성공적으로 반환된 후에 동작 |
@AfterThrowing | 메서드에서 예외가 발생한 후에 동작 |
@Around | 메서드 실행 전후에 동작을 수행, 메서드 실행을 직접 제어 |
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.UserService.getUser(..))")
public void logBeforeUserGet() {
System.out.println("Getting user...");
}
}
이런식으로 @Aspect
로 "내가 공통 모듈이야~" 라고 정의를 한 뒤에,
@Before
와 포인트컷 포현식으로 실행할 시점과 기준점을 정의했다.
포인트컷(Pointcut) 표현식
특정 조건에 따라 어드바이스(Advice)가 적용될 메서드를 필터링하는 데 사용
특정 메서드에 어드바이스를 적용할 때 사용한다.
@Before("execution(* com.example.service.UserService.getUser(..))")
/*
*: 리턴 타입을 의미, *는 모든 타입 (void, String, User 등)
com.example.service.UserService: 클래스 전체 패키지 경로
getUser(..): 메서드를 타겟
..: 0개 이상의 모든 매개변수
*/
execution(public * *(..))
execution(* set*(..))
set
으로 시작하는 모든 메서드에 적용특정 클래스나 패키지 내에서 필터링한다.
@Aspect
@Component
public class LoggingAspect {
// com.example.service 패키지의 모든 클래스의 메서드에 대해 동작
@Before("within(com.example.service.*)")
public void logBeforeMethod() {
System.out.println("[AOP] com.example.service 내 Method 실행 전 입니다.");
}
}
within(com.example.service.*)
service
내의 모든 클래스에 적용.within(com.example.service..*)
service
와 하위 패키지에 있는 모든 클래스에 적용within(com.example.MyClass)
특정 어노테이션이 적용된 Bean의 메서드에 필터링한다.
@Aspect
@Component
public class LoggingAspect {
// @Repository 애너테이션이 붙은 객체가 매개변수로 전달될 때 동작
@Before("@args(org.springframework.stereotype.Repository)")
public void logBeforeRepositoryArg() {
System.out.println("[AOP] @Repository parameter가 호출되었습니다");
}
}