- 시작하게 된 계기 및 다짐 😮
이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프
에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.
- 학습 목표 😮
목표 | 결과 |
---|---|
타입별 Pointcu 표현식, JointPoint 등의 의미 이해 | O |
에너테이션을 이용한 AOP에 대해 이해 | O |
- 정리
1. 포인트컷과 표현식 & 지시자
- 포인트컷은 어드바이스가 실행되는 시기를 제어
- AspectJ는 포인트컷을 편하게 표현하기 위한 특별한 표현식을 제공한다.
- AspectJ : AspectJ pointcut expression
- [ex. @Pointcut("execution(* hello.aop.order..*(..))")]
[예제 Code]
@Pointcut("execution(* transfer(..))") // 포인트컷 표현식
private void anyOldTransfer() {} // 포인트컷 서명
==> 이를 어드바이스에 적용
[ex. @Around(anyOldTransfer())]
2. 포인트컷 지시자
- 포인트컷 지시자(PCD,Pointcut Designater) : execution같은 표현식
- execution을 가장 많이 사용하고 나머지는 잘 사용하지 않는다.
1). execution : 메서드 실행 조인트 포인트를 매칭한다.
스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다.
2). within : 특정 타입 내의 조인 포인트를 매칭한다.
3). args : 인자가 주어진 타입의 인스턴스인 조인 포인트
4). this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
5). target : Target 객체(스프링 AOP 프록시가 가르키는 실제 대상)를 대상으로 하는 조인 포인트
6). @target : 실행 객체의 클래스에 주어진 타입의 애너테이션이 있는 조인 포인트
7). @within : 주어진 애너테이션이 있는 타입 내 조인 포인트
8). @annotation : 메서드가 주어진 애너테이션을 가지고 있는 조인 포인트를 매칭
9). @args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애너테이션을 갖는 조인 포인트
10). bean : 스프링 전용 포인트컷 지시자이고 빈의 이름으로 포인트컷을 지정한다.
3. Pointcut 표현식 결합
- &&, ||, !를 사용하여 결합할 수 있다.
- 이름으로 pointcut 표현식을 참조 할 수 있음
[예제 Code]
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} // (1)
(1) anyPublicOperation은 메서드 실행 조인 포인트가 공용 메서드의 실행을 나타내는 경우 일치
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} // (2)
(2) inTrading 메서드 실행이 거래 모듈에 있는 경우에 일치
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} // (3)
(3) tradingOperation은 메서드 실행이 거래 모듈의 공개 메서드를 나타내는 경우 일치
4. 일반적인 pointcut 표현식들
1). 모든 공개 메서드 실행
- execution(public * *(..))
2). set 다음 이름으로 시작하는 모든 메서드 실행
- execution(* set*(..))
3). AccountService 인터페이스에 의해 정의된 모든 메소드의 실행
- execution(* com.xyz.service.AccountService.*(..))
4). service 패키지에 정의된 메서드 실행
- execution(* com.xyz.service.*.*(..))
5). 서비스 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
- execution(* com.xyz.service..*.*(..))
6). 서비스 패키지 내의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- within(com.xyz.service.*)
7). 서비스 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- within(com.xyz.service..*)
8). AccountService 프록시가 인터페이스를 구현하는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- this(com.xyz.service.AccountService)
9). AccountService 대상 객체가 인터페이스를 구현하는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- target(com.xyz.service.AccountService)
10). 단일 매개변수를 사용하고 런타임에 전달된 인수가 Serializable과 같은 모든 조인 포인트 (Spring AOP에서만 메소드 실행)
- args(java.io.Serializable)
11). 대상 객체에 @Transactional 애너테이션이 있는 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- @target(org.springframework.transaction.annotation.Transactional)
12). 실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트 (Spring AOP에서만 메서드 실행)
- @annotation(org.springframework.transaction.annotation.Transactional)
13). 단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트(Spring AOP에서만 메서드 실행)
- @args(com.xyz.security.Classified)
14). tradeService 라는 이름을 가진 스프링 빈의 모든 조인 포인트 (Spring AOP에서만 메서드 실행)
- bean(tradeService)
15). 와일드 표현식 *Service 라는 이름을 가진 스프링 빈의 모든 조인 포인트
- bean(*Service)
[extra]
JDK Dynamic Proxy
- Interface를 기반으로 Proxy를 생성하는 방식
- 그렇기에 Interface를 강제화 한다는 단점이 있다.
CGLIB Proxy
- Enhancer를 바탕으로 Proxy를 구현하는 방식이다.
- Reflection을 사용하지 않고 extends(상속)방식을 이용해서 Proxy할 메서드를 오버라이딩 하는 방식
Reflection API
- 구체적인 클래스 타입을 알지 못해도 그 클래스의 정보(메서드,타입,변수 등등)에 접근할 수 있게 해주는 API
- [참조] https://tecoble.techcourse.co.kr/post/2020-07-16-reflection-api/
1. AOP 적용 위치
- 메서드 실행 위치 외 다양한 위치에 적용가능
- 조인포인트 : 생성자, 필드 값 접근, static 메서드 접근, 메서드 실행
- AOP를 수행하는 메소드는 이 JoinPoint 인스턴스를 인자로 받게됨
- JoinPoint 인스턴스에서 조인 포인트 지점의 정보를 얻어내야 함
2. JoinPoint
- 어드바이스가적용될 수 있는 위치 같은 프로그램 실행 중 지점을 나타냄
- AspectJ를 사용해서 컴파일 시점/클래스 로딩 시점에 적용하는 AOP는 바이트코드를 실제 조작하기 때문에 해당 기능을 모든 지점에 다 적용 가능
- 프록시 방식을 사용하는 Spring AOP는 메서드 실행 지점만 (조인포인트로 적용가능)AOP 적용가능
- [프록시는 메서드 오버라이딩 개념으로 동작]
- 생성자, static 메서드, 필드값 접근 에는 프록시 개념이 적용될 수 없음
- 프록시 방식을 사용하는 스프링 AOP는 스프링 컨테이너가 관리할 수 있는 스프링 빈에만 AOP를 적용가능
- JoinPoint메소드는 기본적으로 어드바이스 매개변수로 선언하면 된다.
3. JoinPoint 인터페이스의 주요 기능
- JoinPoint.getArgs() : JoinPoint에 전달된 인자를 배열로 반환합니다.
- JoinPoint.getThis() : AOP 프록시 객체를 반환합니다.
- JoinPoint.getTarget() : AOP가 적용된 대상 객체를 반환합니다.
[클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체를 반환합니다.]
- JoinPoint.getSignature() : 조언되는 메서드에 대한 설명을 반환합니다.
1). 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체를 반환합니다
2). Signature
[ 객체가 선언하는 모든 연산은 연산의 이름, 매개변수로 받아들이는 객체들을 시그니처라고 합니다.]
3). Signature가 제공하는 메서드
String getName() : 클라이언트가 호출한 메소드의 이름을 반환합니다.
String toLongString() : 클라리언트가 호출한 메소드의 리턴타입, 이름, 매개변수를 패키지 경로까지 포함해서 반환합니다.
String toShortString() : 클라이언트가 호출한 메소드 시그니처를 축약한 문자열로 반환합니다.
- JoinPoint.toString() : 조언되는 방법에 대한 유용한 설명을 인쇄합니다.
4). ProceedingJoinPoint 인터페이스의 주요 기능
- proceed() : 다음 어드바이스나 타켓을 호출합니다.
★ 스키마 기반 접근, @AspectJ 에너테이션 스타일
1. @AspectJ 지원
- 에너테이션이 있는 일반 Java클래스로 관점을 선언하는 스타일
- Spring은 pointcut 구문 분석 및 일치를 위해 AspectJ제공 라이브러리를 사용하여 AspectJ 5와 동일한 에너테이션을 해석한다.
- AOP 런타임은 여전히 순수한 스프링 AOP이며, AspectJ 컴파일러나 위버에 의존X
2. AspectJ 지원 활성화
- Spring에서 @AspectJ를 사용하기 위해 AOP설정과 이런 aspect에 의해 조인되는 자동 프록시 빈에 대한 Spring 지원을 활성화 해야됨
- XML or JAVA스타일 설정으로 활성화 가능
[예제 Java_Code]
@Configuration
@EnableAspectAutoProxy
public class AppConfig{
}
3. Aspect 선언
- @AspectJ 지원이 활성화되면 @AspectJ 관점(@Aspect 애너테이션이 있음)이 있는 클래스로 애플리케이션 컨텍스트에 정의된 모든 빈이 Spring에서 자동으로 감지되고 Spring AOP를 구성하는 데 사용됩니다.
[예제 Code]
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
4. 포인트컷 선언
- 조인 포인트를 결정하므로, 어드바이스가 실행되는 시기를 제어할 수 있음
- Spring AOP는 Spring Bean에 대한 메소드 실행 조인 포인트만 지원이 되어 Pointcut은 Spring Bean의 메소드 실행과 일치하는 것으로 생각
- pointcut 선언은 이름/매개변수를 포함하는 서명과 우리가 관심 있는 메소드 실행을 결정하는 pointcut표현식 두 부분으로 구성
- pointcut 표현식은 @Pointcut 어노테이션을 사용하여 표시
5. 어드바이스 선언
- 어드바이스는 포인트컷 표현식과 연관되며 포인트컷과 일치하는 메서드 실행 전후 또는 전후에 실행 됩니다.
- pointcut 표현식은 pointcut에 대한 단순참조 or 제자리에 선언된 pointcut 표현식일 수 있다.
- 피드백 😮
실제 Spring에서 AOP(Aspect)가 적용될 수 있는 JointPoint들과 이를 어디에 적용하는지 선택하는 Pointcut을 이용하여 원하는 곳에 Aspect를 적용하는 방법을 알아보았다.
추가로 advice에는 포인트컷을 적용하는 메서드에서 이를 적용할 시점을 추가적으로 선택하여 적용 할 수 있다.
해당 메서드들과 애너테이션들을 실제 적용시키면서 이를 어떤식으로 사용해야할지 익숙해지고 코드들 자체도 외우기보다는 익숙해져야 할 것 같다.
- 앞으로 해야 될 것 😮