티스토리 뷰

스프링 Proxy 기반 AOP (1)

스프링을 통해 애플리케이션을 개발할 때, 우리는 애플리케이션의 도메인과 관련된 코어 로직 개발뿐만 아니라 이 코어 기능을 제대로 동작시키기 위해 트랜잭션 관리나 로깅, 캐싱등의 관심에도 신경써야한다.

하지만 이런 부가적인 관심사들을 도메인 코어 로직에 같이 녹여야한다면 해당 모듈은 변경의 이유가 도메인 로직의 변경 뿐만이 아니게 된다. 그리고 이런 부가적인 관심사들은 전 애플리케이션의 영역이 활용이 필요하다.

그래서 스프링에선 AOP를 통해서 이런 공통적인 기능들을 별도로 분리해 필요한 곳에서 재사용가능하게 했다.


스프링에서 AOP를 적용하기 위해선 스프링에서 자체적으로 구현하고 있는 AOP 프레임워크를 사용할 수도 있고 AspectJ라는 라이브러리를 활용할 수도 있다. 그중에서 스프링의 AOP와 관련된 내용에 대해서 조금씩 알아보고자 한다.



Spring's Proxy-Based AOP Framework

스프링의 AOP 프레임워크는 Proxy 기반으로 동작한다.

Proxy는 컴퓨터쪽에서 많이 사용되는 단어로 다음과 같이 정의된다.

* a person who has been given the authority to represent somebody else

누군가를 대신해서 무언가를 대신 수행해주는 사람, 대리인이라는 뜻을 가지며 주로 네트워크와 디자인 패턴에서 많이 언급 되고, 스프링은 AOP를 위해서 Proxy 패턴을 사용하고 있다.

스프링 AOP가 Proxy 기반으로 동작한다는 것은, AOP를 적용을 원하는 기능을 수행하는 타겟 오브젝트 그 앞단에 proxy 오브젝트를 하나 더 세워 그 proxy가 부가 기능을 수행하고 실제 오브젝트로의 호출까지 담당하는 것이다.


스프링 AOP는 두가지 방식으로 타겟 오브젝트의 프록시를 만든다.

  • JDK Dynamic Proxy: Java에서 기본적으로 제공하는 표준 프록시 생성 모듈. 인터페이스 기반의 프록시 생성
  • cglib: 오픈소스 라이브러리. 클래스 기반의 프록시 생성
    • cglib의 코드를 래핑해서 스프링 코드의 일부로 포함시킴.

spring reference 이미지 참조
aop-proxy-call

self-invocation

그래서 개발하는 애플리케이션에서 스프링 빈에 aop를 걸고자 하면 스프링이 런타임에 해당 빈에 프록시를 생성해주고,
해당 빈을 사용하고자 하는 다른 빈에선 그 프록시를 주입받아 프록시 객체로 호출을 요청한다.
(프록시 호출을 통해 실제 target 오브젝트의 메소드 호출 전후로 원하는 공통 기능을 수행함.)


위그림에서 볼 수 있고, 스프링 레퍼런스에서도 언급하는 aop 관련 주의사항이 self-invocation인데, target 내부에서 target 자신의 메소드를 호출하게되면 aop가 먹히지 않는다. (target 내부에서 내부를 호출하는 것이기 때문에 프록시를 거치지 않음)


공식 레퍼런스에선 self-invocation 이슈를 피하기위해서 AspectJ(non-proxy based)를 사용하라고 하긴 한다.(하지만 실제 운영중인 서비스에서 aop방식을 바꿨을 때 일어날 수 있는 사이드이펙트때문에 쉽지 않을 듯)


현재 개발하고 있는 팀에선 레거시 코드속에서 self invocation이 어쩔 수 없이 일어나는 경우를 위해 ApplicationContext에서 빈 자기 자신을 꽂아서 호출하도록 하고 있다.(좀더 좋은 방법 없을까)




[기타] 스프링 부트에서의 프록시 방식

스프링 부트에서의 프록시 방식의 디폴트로 cglib를 선택하고 있고, 이와 관련해서 히스토리를 조금 찾아봤고 내용은 아래와 같다.

  1. 부트 프로젝트에서는 트랜잭션 AOP 관련해서 인터페이스를 사용하지 않았을 때의 프록시 관련 이슈를 막고자 1.4부터 애노테이션 기반 트랜잭션을 활성화하는 @EnableTransactionManagement의 proxyTargetClass 옵션의 디폴트를 true로 두어 프록시 방식을 cglib로 고정하도록 함.
    • default로 true를 고정하는게 side effect가 없을 것으로 판단함.(cglib는 어쨌든 스프링 내부에서 repackaging되어 관리되기때문에)
  2. 부트에서 jdk 다이내믹 프록시를 쓰려고 응용단에서 설정(spring.aop.proxy-target-class)을 조작해봤지만 무시되고 부트에서 cglib로 꽂아버림. -> 1.5.3에서 부트쪽 트랜잭션 auto configuration 클래스에서 spring.aop.proxy-target-class 속성에 따라 핸들링 될 수 있도록 수정.
  3. 2.0에서 트랜잭션뿐 아니라 spring aop의 proxy-target-class default 설정을 false -> true로 바꿔버림, code


다음에는 cglib의 동작 방식과 스프링에서 활용되는 cglib 프록시 객체를 통한 트랜잭션(애노테이션 기반) AOP의 동작방식에 대해 알아보고자 한다.

참조

'Server Side > Spring' 카테고리의 다른 글

스프링 프록시 기반 AOP (2) - CGLIB Proxy  (1) 2020.05.01
스프링 @Async와 ThreadPoolTaskExecutor  (0) 2019.02.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함