스프링 AOP
31 Mar 2021AOP
관점 지향 프로그래밍 방법론으로, 관심사를 나눠(SoC) 모듈성을 증가시키는 것이 목적인 프로그래밍 패러다임이다.
관심사를 왜 나눌까?
- 인간의 인지능력은 한계가 있다. (한 번에 생각할 수 있는 양에 한계가 있음)
- 모든 소프트웨어 개발의 핵심은 복잡성을 극복하는 것이다.
- 즉, 한 번에 여러가지를 동시에 신경 쓰면 복잡하니깐, 각각을 따로 분리해서 생각하자는 이야기.
관점에 따라 나눠보기
- Primary(core) Concern
- 주 업무 관심 (비지니스 로직)
- Cross-cutting Concern
- 횡단 관심
- 로깅, 보안처리, 트랜잭션처리 등..
여기서 Concern
은 관심사를 의미한다.
대략적인 도식화는 다음과 같다.

그림 1
스프링은 Proxy
기반의 AOP 를 제공한다.
조금 더 자세한 함수 호출 방식은 다음과 같다.

그림 2
스프링 프록시 자세하게 알아보기
- 스프링은 프록시 기반의 AOP를 제공하며, 이를 구현하기 위해
Java Dynamic Proxy
또는Cglib
사용. AopProxy
라는Delegator
인터페이스로 표현.- Dynamic proxy 기반은
JdkDynamicAopProxy
클래스 - Cglib 기반은
CglibAopProxy
클래스 - cf)
DefaultAopProxyFactory
를 이용해 즉시 사용 가능한 구현체 생성 가능.
createAopProxy()
메서드를 통해 AopProxy 실 구현체 생성.- AOP 적용 대상이 인터페이스 or 인터페이스를 구현 ->
JdkDynamicAopProxy
생성됨. - else ->
ObjenesisCglibAopProxy
기반 실 객체가 생성됨. - cf)
Objenesis
는 과거 Cglib 단점을 해결해 주는 라이브러리이다.
- AOP 적용 대상이 인터페이스 or 인터페이스를 구현 ->
- Dynamic proxy 기반은
- Dynamic proxy 기반과 Cglib 기반은 프록시 객체 생성 방식 에 차이가 있다.
- Dynamic proxy 기반은 프록시 객체 생성을 위해 인터페이스 필수 구현.
- Cglib 기반은 인터페이스를 구현하지 않은 일반 클래스 에 런타임 시 코드 조작으로 프록시 객체를 생성.
- 스프링 AOP 가 제공하는 프록시 기반 방식은 런타임 위빙(RTW) 방식이다.
- 프로그램 구동 중에 위빙(코드 삽입)이 일어남.
- 반면, AspectJ 는 컴파일 위빙(CTW) or 로드 타임 위빙(LTW) 이용. (런타임 위빙보다 성능이 우세)
- AspectJ는 기본 스프링 AOP 보다 다양한 포인트컷 지원.
- Transaction 대상의 경우 기본적으로 Cglib 으로 생성 됨.
- 성능상 Cglib 우세, 예외발생 확률도 적음.
- Cglib 기존 문제점(생성자 중복 호출, default 생성자 필요 문제 등)을
Objenesis
라이브러리가 해결.
- AspectJ 를 사용하기 위해서는 AJC 등 별도의 컴파일러 설정 필요하나 스프링 AOP는 그렇지 않음. (성능도 크게 체감하기 힘들다고 함)
Bean을 Proxy로
- 스프링에서 Bean 으로 등록된(=BeanDefinition에 사전 정의된) 객체는 IoC 컨테이너에서 관리된다.
Bean 객체에 대해 스프링 AOP는 어떻게 Proxy를 생성하고 관리할까?

그림 3
Bean 후처리기
- 스프링은 자동 Proxy 생성을 위해
DefaultAdvisorAutoProxyCreator
클래스를 Bean 후처리기에 등록하여 사용한다.- Advisor 를 이용한 자동 프록시 생성기이다.
- Bean 객체 일부를
Proxy
로 포장하고,Proxy
를 Bean 대신 IoC 컨테이너에 등록한다.- 스프링은 Bean 생성 마다 후처리기에 전달.
- 후처리기는 Bean 으로 등록된 모든 Advisor 내 PointCut 을 이용하여 전달받은 Bean이 프록시 적용 대상인지 확인.
- 내장된 Proxy 생성기를 통해 Bean 에 대한
Proxy
생성, Advisor 연결. - 전달 받은 Bean 대신
Proxy
를 Ioc 컨테이너에 등록 시킴. - 컨테이너는 Bean 후처리기가 돌려준
Proxy
를 Bean 으로 등록.