AOP
설명
- AOP 는 관점 지향 프로그래밍이다. 프로그램 내 여러 모듈에 작동되는 부가적인 로직을 핵심로직으로부터 분리하여 모듈화할 수 있다. 유지보수성이 향상된다.
Spring AOP
- Spring AOP는 데코레이션 패턴을 구현한 프록시 기반으로 런타임에서만 AOP가 작동한다.
- 데코레이터 패턴이란 요청자와 제공자 사이에서 중간자 역할을 하는 프록시가 제공자에게 요청자의 메시지가 전달되기 전과 제공자의 응답 메시지가 요청자에게 전달될 때 특정 로직(명령)을 수행하도록 하는 패턴이다.
- 요청자 --(메시지)-> 제공자
- 요청자 <--> 프록시 <--> 제공자
- 이 프록시에서 메시지를 조작하거나, 로그를 찍거나, 트랜잭션을 관리하는 로직이 들어갈 수 있다.
- 이렇게 특정 시점을 기준으로 로직이 실행되도록 하는 것이 AOP 이다.
- 스프링 AOP는 스프링의 빈후처리기를 통해 빈이 생성될 때 pointcut들에 해당하는 빈이면 프록시를 생성한다. 여러 advice가 있다면, 어드바이스 체이닝을 통해 적용한다. (필터 체이닝과 인터셉터 체이닝 작동방식과 유사하다)
- 빈후처리기란 이름 그대로 빈을 생성한 다음에 실행되는 로직이다.
- Spring AOP 의 빈후처리기
AnnotationAwareAspectJAutoProxyCreator
소스코드
AOP 용어
- 요청자 --> 포인트컷(어드바이스 적용 대상 확인) --> 어드바이스(부가로직 실행) --> 타겟
- 어드바이스 advice 란 부가로직을 실행하는 주체이다.
- 포인트컷 pointcut 이란 어드바이스(부가로직)이 적용될 시점을 선별하는 주체. 주로 aspectJ 표현식을 사용.
- 조인 포인트 joinpoint 는 어드바이스가 적용될 시점. AOP 용어로 추상적인 개념의 단어.
- 스프링 AOP 에서는 메소드 호출 시점와 메소드 반환 시점 밖에 없다.
- AspectJ 에서는 메소드 호출과 반환 시점 뿐만 아니라, 실제 메소드 실행 시점(자기 호출 가능), 생성자 실행 시점, 생성자 실제 실행 시점, static 초기화 실행 시점, 필드 참조 시점, 필드 set 시점 등 다양하게 지원한다.
- Join Points, eclipse.org/aspectj
- Aspect 은 advice 와 pointcut 를 모듈화한 것. Spring AOP 에선 @Aspect 어노테이션 클래스로 설정. 여러 advice와 Pointcut이 함께 존재
- Advisor 는 Spring AOP 용어로 advice와 pointcut을 포함한 구성 모듈.
- Weaving 위빙이란 어드바이스가 타겟에 적용되는 전과정을 통칭. 스프링AOP 위빙은 프록시 기반. AspectJ 는 바이트코드 기반도 가능.
- AOP 용어 정리
Spring AOP, 프록시 기반 AOP 문제
- 프록시 기반 AOP 단점이 있다. 프록시 대상인 객체 메소드 안에서 객체 내 다른 메소드를 self-invocation(자기 호출)을 하면 프록시 객체를 거치지 않고 바로 자신 객체를 호출하기 때문에 AOP 가 적용될 수 없다. 스프링 AOP 위빙의 한계점이다.
- 위빙 weaving 이란 AOP 용어로 부가적 코드(aop advice)가 핵심코드에 적용되는 과정을 통칭한다.
- 자기 호출 문제는 AspectJ 를 사용하여 바이트코드 조작 기반 weaving 위빙을 사용해서 해결할 수 있다.
- .java 소스를 .class 바이트코드로 변환할 때 부가 로직을 핵심 로직에 추가해서 바이트 코드에는 핵심로직+부가로직이 만들어진다. 실행 시, 메소드 내에 있는 부가로직 코드가 실행되기 때문에 문제가 없다.
- Self Invocation은 왜 발생할까?
- Spring Boot2에서 AspectJ 위빙으로 바꿔볼까?
동적 프록시 vs CGLIB 프록시
- 스프링 프록시 객체는 JDK 동적 프록시 또는 CGLIB 프록시 이다.
- JDK 동적 프록시는 JDK에서 프록시 패턴을 구현한 것으로, 인터페이스 대상으로 프록시를 생성할 수 있다. 프록시 객체는 타겟 인터페이스를 구현한 구현체이다.
InvocationHandler
인터페이스를 구현해서 만들 수 있음. - 제약사항은 타겟이 인터페이스가 있어야 한다는 것이다.
- CGLIB 방식 프록시는 타겟 클래스를 상속하여 만든 프록시이다.
MethodInterceptor
인터페이스 구현하여 만들 수 있음- 제약으로는 부모 클래스 생성자를 체크해야 한다. 자식 생성자에서 부모 생성자를 호출하기 떄문.
- final 클래스는 상속할 수 없어서 프록시 생성 불가. final 메서드도 상속할 수 없어 프록시 적용 안됨.
- 스프링에서는 타겟이 인터페이스냐 클래스냐에 따라 자동으로 프록시를 생성해주는
프록시 팩토리 Proxy Factory
를 사용한다.- 하지만 Spring Boot 에서는 기본으로
proxyTargetClass=true
세팅되어 있는데, 타겟이 인터페이스 여부를 무시하고 무조건 CGLIB 로 프록시를 생성하는 설정이다. - 옵션과 무관하게 인터페이스가 없으면 JDK 동적 프록시를 적용할 수 없으므로 CGLIB를 사용한다.
- 기본값이 구체클래스로 기반 CGLIB 되어 있는 이유는 동적 프록시 한계점이 있다.
@Autowired UserServiceImpl userservice
로 되어있는 경우 구현체를 Bean으로 주입해야하는데, proxy 객체는 같은 인터페이스의 다른 구현체이기 때문에 타겟 클래스로 타입캐스팅에 실패해서 주입이 안된다.
- 하지만 Spring Boot 에서는 기본으로
'공부노트 > 스프링' 카테고리의 다른 글
[JPA] Collection, 고아, 트랜잭션, Facade, OSIV (0) | 2022.08.06 |
---|---|
[JPA] N+1 문제 (0) | 2022.08.06 |
AOP / Spring AOP 요약 (0) | 2022.07.16 |
Spring AOP 적용 (0) | 2022.06.25 |
스프링 AOP (0) | 2022.06.19 |