728x90
- AOP (Aspect Oriented Programming)
- aspectjweaver 의존을 추가해야 함 (스프링이 AOP 구현 시 사용하는 모듈)
- spring-context 모듈을 추가하면 spring-aop 모듈도 함께 의존 대상에 포함
- asepctjweaver 모듈은 AOP를 설정하는데 필요한 애노테이션 제공
프록시
- 부가적인 기능 구현을 위해 기존 코드를 수정하지 않고, 코드 중복도 피하기 위해서 프록시 객체 사용
- 핵심 기능의 실행은 다른 객체에 위임하고 부가적인 기능을 제공하는 객체를 프록시라고 부름
- 프록시의 특징이 핵심 기능은 구현하지 않는다는 것
- 핵심 기능을 구현하지 않는 대신 여러 객체에 공통으로 적용할 수 있는 기능 구현
- 실제 핵심 기능을 실행하는 객체는 대상 객체라고 부름
AOP
- 여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법
- 공통 기능 구현과 핵심 기능 구현을 분리하는 것이 AOP의 핵심
- 핵심 기능을 구현한 코드의 수정 없이 공통 기능을 적용할 수 있음
- 핵심 기능에 공통 기능을 삽입하는 방법
- 컴파일 시점에 코드 공통 기능을 삽입하는 방법 (AOP 개발 도구가 소스 코드를 컴파일 하기 전에 소스에 삽입)
- 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입하는 방법
- 런타임에 프록시 객체를 생성해서 공통 기능을 삽입하는 방법 (스프링에서 제공하는 방법)
- 중간에 프록시 객체를 생성하고 실제 객체의 기능을 실행하기 전/후에 공통기능을 호출
- 이때 스프링 AOP는 프록시 객체를 자동으로 생성해줌
용어 | 의미 |
Advice | 언제 공통 관심 기능을 핵심 로직에 적용할 지 정의. 메서드를 호출하기 전(언제)에 트랜잭션 시작(공통 기능) 기능을 적용한다는 것을 정의 |
Joinpoint | Advice를 적용 가능한 지점. 메서드 호출, 필드 값 변경 등이 해당. 스프링은 프록시를 이용해서 AOP를 구현해서 메서드 호출에 대한 Joinpoint만 지원 |
Pointcut | Joinpoint의 부분 집합으로서 실제 Advice가 적용되는 Joinpoint를 나타냄. 정규 표현식이나 AspectJ 문법 이용 |
Weaving | Advice를 핵심 로직 코드에 적용하는 것 |
Aspect | 여러 객체에 공통으로 적용되는 기능. 트랜잭션이나 보안 등 |
Advice의 종류
- 메서드 호출 시점에 Aspect를 적용하기 때문에 구현 가능한 Advice의 종류는 아래와 같다.
종류 | 설명 |
Before Advice | 대상 객체의 메서드 호출 전에 공통 기능을 실행 |
After Returning Advice | 대상 객체의 메서드가 익셉션 없이 실행된 이후에 공통 기능을 실행 |
After Throwing Advice | 대상 객체의 메서드를 실행하는 도중 익셉션이 발생한 경우에 공통 기능 실행 |
After Advice | 익셉션 발생 여부에 상관없이 대상 객체의 메서드 실행 후 공통 기능을 실행 (가장 널리 사용 됨. 다양한 시점에 원하는 기능을 삽입 가능) |
Around Advice | 대상 객체의 메서드 실행 전, 후 또는 익셉션 발생 시점에 공통 기능을 실행하는데 사용 (캐시 기능, 성능 모니터링 기능 구현 시 사용) |
스프링 AOP 구현
- Aspect로 사용할 클래스에 @Aspect 애노테이션 붙이기
- @Pointcut 애노테이션으로 공통 기능을 적용할 Pointcut 정의
- 공통 기능을 구현한 메서드에 @Around 애노테이션 적용
@Aspect
public class ExamAspect{
@Pointcut("execution(public * sample..*(..))") //Advice를 적용할 메서드 지정
private void publicTarget() {
}
@Around("publicTarget()") //publicTarget() 메서드에 정의한 Pointcut에 공통 기능 적용
public Object commonFunc(ProceedingJointPoint joinPoint) throws Throwable {
// ... 공통 로직
Object result = joinPoint.proceed(); // 프록시 대상 객체의 메서드 호출
// ... 공통 로직
}
execution 표현식
execution(수식어패턴? 리턴타입패턴 클래스이름패턴?메서드이름패턴(파라미터패턴))
- 수식어패턴 : 생략 가능. 스프링 AOP는 public 메서드에만 적용할 수 있기 때문에 public만 의미 있음
- 리턴타입패턴 : 리턴타입 명시
- 클래스이름패턴, 메서드이름패턴 : 클래스 이름 및 메서드 이름 명시 (패키지명으로도 표현 가능)
- 파라미터패턴 : 매칭될 파라미터에 대해서 명시
- 각 패턴은 *을 이용해서 모든 값을 표현할 수 있으며, .. 을 이용하여 0개 이상이라는 의미 표현 가능
ProceedingJoinPoint 메서드
- 공통 기능 메서드는 대부분 파라미터로 전달받은 ProceedingJointPoint의 proceed() 메서드만 호출하면 되지만, 다른 정보들이 필요할 때가 있다
- ProceedingJoinPoint 인터페이스가 제공하는 메서드
- Signature getSignature() : 호출되는 메서드에 댛나 정보를 구함
- Object getTarget() : 대상 객체를 구함
- Object[] getArgs() : 파라미터 목록을 구함
- org.aspectj.lang.Signature 인터페이스가 제공하는 메서드
- String getName() : 호출되는 메서드의 이름을 구함
- String toLongString() : 호출되는 메서드를 완전하게 표현한 문장을 구함 (메서드의 리턴 타입, 파라미터 타입이 모두 표시)
- String toShortString() : 호출되는 메서드를 축약해서 표현한 문장을 구함 (기본 구현은 메서드의 이름만을 구함)
프록시 생성 방식
- 빈 객체가 인터페이스를 상속하면, 그 인터페이스를 이용하여 프록시를 생성한다
- 인터페이스가 아닌 실제 클래스 타입으로 getBean()을 통해 빈 객체를 구하면 오류 발생하므로 주의
- 빈 객체가 인터페이스를 상속할 때, 인터페이스가 아닌 클래스를 이용해서 프록시를 생성하고 싶다면 아래와 같이 설정
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppCtx{
//...
}
Pointcut
- 하나의 Pointcut 에 여러 Advice를 적용할 수 있으며, 어떤 Aspect가 먼저 적용될지는 스프링 프레임워크나 자바 버전에 따라 달라질 수 있음
- Aspect 적용 순서가 중요하다면 @Aspect 애노테이션과 함께 @Order 애노테이션을 붙여 적용 순서 결정 가능
@Aspect
@Order(1)
public class FirstAspect {
}
@Aspect
@Order(2)
public class SecondAspect {
}
- @Pointcut 애노테이션이 아닌 @Around 애노테이션에 execution 명시자를 직접 지정할 수도 있음
@Aspect
public class ExamAspect {
@Around("execution(public * package..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
...
}
}
- 여러 Aspect에서 공통으로 사용하는 Pointcut이 있다면 별도 클래스에 Pointcut을 정의하고, 각 Aspect 클래스에서 해당 Pointcut을 사용하도록 구성하면 Pointcut 관리가 편해짐 - @Around("별도 클래스에 정의된 Pointcut 메서드()")
- Pointcut을 설정한 클래스는 빈으로 등록할 필요가 없으며, @Around 애노테이션에서 해당 클래스에 접근 가능하면 해당 Pointcut 사용 가능
'스프링' 카테고리의 다른 글
[스프링] @RequiredArgsConstructor 생성자 주입 애노테이션 (0) | 2023.11.15 |
---|---|
스프링5 프로그래밍 입문 - DB 연동 (0) | 2023.11.05 |
스프링5 프로그래밍 입문 - 빈 라이프사이클과 범위 (0) | 2023.10.02 |
스프링5 프로그래밍 입문 - 컴포넌트 스캔 (0) | 2023.10.02 |
스프링5 프로그래밍 입문 - 의존 자동 주입 (0) | 2023.10.02 |