스프링 AOP



AOP

관점 지향 프로그래밍 방법론으로, 관심사를 나눠(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 단점을 해결해 주는 라이브러리이다.
  • 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 컨테이너에 등록한다.
      1. 스프링은 Bean 생성 마다 후처리기에 전달.
      2. 후처리기는 Bean 으로 등록된 모든 Advisor 내 PointCut 을 이용하여 전달받은 Bean이 프록시 적용 대상인지 확인.
      3. 내장된 Proxy 생성기를 통해 Bean 에 대한 Proxy 생성, Advisor 연결.
      4. 전달 받은 Bean 대신 Proxy 를 Ioc 컨테이너에 등록 시킴.
      5. 컨테이너는 Bean 후처리기가 돌려준 Proxy 를 Bean 으로 등록.