컨텐츠 바로가기

Spring Proxy 활용에 대한 이해 #1 트랜잭션

http://aretias.egloos.com/708477

"Understanding proxy usage in SPRING"
http://blog.springsource.org/2012/05/23/understanding-proxy-usage-in-spring/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+SpringSourceTeamBlog+%28SpringSource+Team+Blog%29
이라는 글을 읽고 굉장히 마음에 드는 글이라는 생각이 들어 정리를 해보기로 하였다.

Spring framework에서 Proxy 기술은 매우 많은 기술 요소들에서 사용되고 있다고 하며, 원문에서는 그에 대해 Transaction과 Caching, 그리고 Java Configuration(Java Code를 이용한 Bean설정)을 통해 알아보고자 한다.

나는 이것을 각각에 대해서 3번에 나누어 정리할 것이다.


Transaction
transaction에서 어떻게 proxy가 활용되는지에 대해서 이해하기 위해서 원문은 2가지 단계로 나누어 설명을 하고 있다.

1. Service Class에 트랜잭션이 아직 정의되지 않은 상태를 살펴본다.
2.Service Class에  트랜잭션을 정의하고 난 후 상태를 살펴본다.


그럼, 1단계부터 보자.
@Service
public class AccountServiceImpl  implements AccountService {
 //…
 
// 트랜잭션 정책이 정의되지 않은 상태
 public void create(Account account) {
 entityManager.persist(account);
 }
}


위의 코드에 있는 create 메소드는 앞서 말한 바와 같이 트랜잭션이 정의되지 않은 상태이다. 
그리고,  create메소드 안에서 수행되는 비즈니스 로직은 예외를 발생시킬 가능성이 있다. 

이것의 런타임 시에 동작 구조는 아래와 같다.

(AccountService는 Interface이고, AccountServiceImpl은 그에 대한 구현체이다.)
아직 proxy가 적용되지 않은 상태이며 AccountServiceImpl 클래스의 인스턴스로 create 메소드에 대한 호출이 바로 전달될 것이다.



이번에는 2단계로 트랜잭션을 설정하였을 때를 보자.

<Implementation Code>
@Service
public class AccountServiceImpl  implements AccountService {

 @Transactional
 public void create(Account account) {
 entityManager.persist(account);
 }
 
}

<Spring Configuration>

<bean id="transactionManager">
    // Datasource 같은 것으로 보면 됨.
 <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
 
<tx:annotation-driven/>


구현 코드를 살펴보면 @Transactional이 추가된 것을 볼 수 있으며, Spring Configuration에서는 Transaction Manager에 대한 설정과 tx:annotation-driven을 볼 수 있는데
<tx:annotation-driven /> 설정이 존재하게 되면 startup time에 모든 @Transactional annotation을 스캔하는 작업을 하고 스캔된 대상 메소드들은 트랜잭션 처리가 되게 된다.

그렇다면, 그 "트랜잭션 처리"는 어디서 되게 되는 것인가?


- 어플리케이션이 startup 되기 전에는 위의 코드는 전자의 코드와 동일한 형태로 이다.


그리고, 어플리케이션이 startup되면 "PROXY"라라고 불리는 새로운 클래스가 생성되게 된다. 
그리고 그 PROXY 클래스에서 트랜잭션에 대한 처리를 담당한다.


(점선은 인터페이스의 구현인 implmentation을 나타낸다.)

생성된 proxy class는 AccountserviceImpl을 덮어 감싸고 있는 존재가 된다.
그리고, 그 트랜잭션 처리는 다음과 같은 형태로 이루어 진다.

1. Caller에서 proxy로 create메소드 호출
2. proxy에서 transaction 시작( autoCommit(false) )
3. proxy에서 accountServiceImpl로 create 메소드 호출
4. proxy에서 transaction 후 처리 ( commit 또는 rollback)

(물론 이는 이해를 돕기 위한 설명임을 명심하기 바란다. 실제로 안에서의 동작은 보다 복잡하며 트랜잭션에 대한 처리는 TransactionManager를 통해서 이루어진다.)

자, 그럼 보다 가시적으로 이해를 해보기 위해서 다음과 같은 코드를 삽입해본다.


AccountService accountService = (AccountService) applicationContext.getBean(AccountService.class);
String accountServiceClassName = accountService.getClass().getName();
System.out.println(accountServiceClassName);

ApplicationContext를 통해서 Bean Container로부터 interface인 AccountService에 대한 빈을 요청하고 요청된 Bean에게 getClass()를 하여
실제 클래스명을 logging하고 있다.

그 결과는 무엇일까?
일반적인 Transaction이 적용되지 않은 1번 상황이라면 아래와 같은 결과가 나올 것이다.

AccountServiceImpl
하지만 실제 결과는 아래와 같다.
$Proxy13
$Proxy13이라는 결과는 물론 사람마다 조금씩 다를 수 있다. 
중요한 것은 AccountServiceImpl가 아니며, 심지어 실제로 존재하는 클래스의 이름조차 아니라는 것이다.

$Proxy13은 Sirng에서 JDK의 Reflection API를 이용하여 생성한 Dynamic Proxy(more information here).의 결과로 생긴 클래스이다.
이렇게 생성된 Proxy클래스들은 당연히 어플리케이션이 startup 될때에 생겻듯이 shutdown될 때에 사라진다.


내일은 Caching부분을 정리해보도록 하겠다.


덧글|덧글 쓰기|신고