@Transactional..???
Spring이 알아서 해주잖아..?
Spring + JPA 로 개발하다 보면 눈에 보이지 않는 것들에 대해서 놓치고 개발하는 경우가 빈번하게 생깁니다.
생각보다 많은 개념이 들어가 있는 @Transactional
조차도 말 그대로 Spring느님께서 다 알아서 해주니까요 😅
그러다가 장애 나거나 커스터마이징이 필요하다면...?
그러다 어느 순간 커스타마이징, 장애 등의 상황에서 대응하려고 보면 난감한 상황에 놓이게 됩니다.
그래서 구글링을 시작하지만, Spring이 알아서 해주던 영역이기 때문에 처음 보는 단어들뿐입니다.
그래서 Spring안에서 어떤 방식으로 @Transactional
이 동작하는지 눈으로 확인해보려 합니다.
@Transactional !!!!
AOP Transaction 관련 Advisor
@Transactional
은 AOP를 통해 Proxy형태로 실제 로직의 전/후에 transaction관련 로직이 실행되게끔 동작합니다.
public void realLogic(){
try {
transactionBegin(); // AOP로 생성된 트랜잭션 시작
super.realLogic(); // 실제 로직
commit(); // AOP로 생성된 커밋
} catch(.... e) {
rollback(); // AOP로 생성된 롤백
}
}
위 형태로 AOP가 동작하려면 최소 하나 이상의 Advisor가 설정되어야 할 것입니다.
어디서 Transaction 관련 Advisor가 설정되는지부터 살펴보겠습니다.
ProxyTransactionManagementConfiguration
Transaction관련 Advisor는 ProxyTransactionManagementConfiguration
에서 찾을 수 있습니다.
해당 Configuration중 transactionAdvisor()
에서 BeanFactoryTransactionAttributeSourceAdvisor
가 생성되어 bean에 등록되는 것을 확인할 수 있습니다.
// ProxyTransactionManagementConfiguration.java
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
그리고 BeanFactoryTransactionAttributeSourceAdvisor
를 구성하는 Pointcut과 Advice에 관한 설정을 확인해봐야 합니다.
- Pointcut
- Pointcut은
TransactionAttributeSourcePointcut
의matches()
를 기준으로 method의 Transaction 설정 유무를 판단합니다. - Pointcut은
BeanFactoryTransactionAttributeSourceAdvisor
내부에서 익명 클래스 형태로 직접 생성해서 사용하고 있습니다.
- Pointcut은
// BeanFactoryTransactionAttributeSourceAdvisor.java
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
@Override
@Nullable
protected TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
};
- Advice
- Advice는
BeanFactoryTransactionAttributeSourceAdvisor
의 상위 클래스인AbstractBeanFactoryPointcutAdvisor
의setAdvice()
를 통해 외부에서 설정됩니다.(ProxyTransactionManagementConfiguration
의 advisor.setAdvice(transactionInterceptor); 참고) - 그리고 해당 Advice를 이용하여 Transaction 관련 로직들이 실행됩니다.
- Advice는
Advisor에서 사용하는 Advice의 구현체
Adivce를 설정하는 ProxyTransactionManagementConfiguration
에서 Advisor의 Advice를 설정하는 구문을 확인할 수 있었습니다.
Advisor는 어디선가 TransactionInterceptor
라는 객체를 받아와서 Advice 설정하고 있습니다.
이 TransactionInterceptor
는 어디서 생성되어서 주입되는지 확인해보겠습니다.
Advice를 담당하는 TransactionInterceptor가 생성되는 곳
TransactionInterceptor
는 Advisor와 동일하게 ProxyTransactionManagementConfiguration
내부에서 생성됩니다.
// ProxyTransactionManagementConfiguration.java
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
TransactionInterceptor
를 생성하는 로직 중 interceptor.setTransactionManager(this.txManager)
를 보면 TransactionManager
라는것을 설정하는 과정이 있는 것을 확인할 수 있습니다.
그럼 이제 이 TransactionManager
가 TransactionInterceptor
에서 무슨 역할을 하는지 알아보겠습니다.
TransactionManager
TransactionManager
가 무엇인지부터 살펴보겠습니다.
사실 TransactionManager
는 빈 interface입니다.
“이 클래스는 TransactionManager 이다”라는 표시의 의미만 가지는 빈 interface입니다.
실제 구현체 소스코드를 봐도 아무 내용이 없는 interface입니다.
// TransactionManager.java 전문
public interface TransactionManager {
// 아무 내용도 없음
}
저희가 중요하게 살펴봐야할것은 TransactionManager
의 구현체인 PlatformTransactionManager
와 AbstractPlatformTransactionManager
입니다.
PlatformTransactionManager
와 AbstractPlatformTransactionManager
는 위 사진처럼 계층구조를 가지고 있습니다.
PlatformTransactionManager
// PlatformTransactionManager.java
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
PlatformTransactionManager
는 Transaction 관리에 기본적으로 필요한 트랜잭션 생성
, 커밋
, 롤백
3가지 기능을 가집니다. 그리고 이 3가지를 Spring 컨테이너 설정대로 동작할 수 있도록 추상 클래스 형태로 최소 기능만 구현해놓은 형태가 AbstractPlatformTransactionManager
입니다.
💡 PlatformTransactionManager에서 제공하는 interafce의 형태가 void 혹은 TransactionStatus만을 반환하도록 되어있습니다.
- 단순하게만 생각해보면 Transaction을 의미하는 객체를 반환하거나 commit, rollback의 매개변수로 전달해야 할 것 같지만 void이거나 엉뚱한 TransactionStatus 를 주고받도록 되어있습니다.
- 하지만 Spring의 트랜잭션 관리를 다시 생각해보면 Thread단위로 Transaction이 관리되고 이는 ThreadLocal이라는 공간에서 관리되는 것을 생각해봤을 때 인터페이스 단계에서 Transaction 객체를 주고 받을 필요는 없다는 것을 유추할 수 있습니다.
(하단 AbstractPlatformTransactionManager의 메소드들도 동일한 이유)
AbstractPlatformTransactionManager
PlatformTransactionManager
도 Spring 컨테이너 안에서 동작하도록 추상화되어 있지만 추상화의 수준이 높아 구현체에서 작성해줘야 할 로직들의 양이 상당합니다.
그래서 특정 공통 로직들에 한해서 구체화한 형태가 AbstractPlatformTransactionManager
입니다.
// AbstractPlatformTransactionManager.java 의 추상 메소드
protected abstract Object doGetTransaction() throws TransactionException;
protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException;
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
AbstractPlatformTransactionManager
는 위 처럼 4가지의 추상 메소드를 가지면서 PlatformTransactionManager
의 3가지 인터페이스를 모두 구현하고 있습니다.
// AbstractPlatformTransactionManager.java 중...
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
Object transaction = doGetTransaction();
// doGetTransaction() : 각 low level 트랜잭션 기술이 실제로 구현해야 할 추상 메소드
//......
}
위 코드에서 확인할 수 있듯이 PlatformTransactionManager
의 getTransaction()
이 Spring 컨테이너 안에서 관리될 수 있을 정도의 로직이 구현되어있습니다. 그리고 각 low level 트랜잭션 기술마다 다를 수 있는 부분을 자체적으로 다시 추상화한 doGetTransaction()
으로 대체하였습니다.
위 내용을 기반으로 각 Persistence 관련 기술들이 구현한 AbstractPlatformTransactionManager
의 구현체들을 찾아보면 아래 사진처럼 우리가 평소에 사용하는 기술들의 이름을 쉽게 찾을 수 있습니다.
ex) JdbcTransactionManager, HibernateTransactionManager, JpaTransactionManager 등등..
다시 TransactionInterceptor 안에서의 TransactionManager의 역할
- 기본적으로 Spring의 Transaction 관리 중
TransactionInteceptor
는 Adivce의 역할을 합니다. - 이 Advice의 실제 구현체를 작성하기 위해서는 각기 다른 Transaction 기술들의 Transaction 관리 로직이 필요하게 됩니다.
- 각기 다른 Transaction 관리 로직들을 유기적으로 동작하게 하기 위해 추상화한 형태가
TransactionManager
입니다. - 그리고 이
TransactionManager
를 Spring 컨테이너에 맞게 한 단계씩 구체화해가면서 일부분을 다시 추상화한 형태가PlatformTransactionManager
와AbstractPlatformTransactionManager
입니다.
그렇다면 일반적인 JPA에서는 어떤 설정으로 동작하는거지?
위 내용을 통해 Spring Transaction관리의 추상화 기술들에 대해 알 수 있었습니다.
추상화 기술들에 대해 알아봤으니 이제 일반적인 JPA세팅에서 어떤 설정으로 동작하는지 좀 더 구체적으로 알아보려 합니다.
그러다 보면 EntityManager, EntityManagerFactory에 대한 내용도 필요하게 됩니다.
구체적인 내용은 다음 글에서 다시 설명을 이어가도록 하겠습니다.
다음글 : https://cobbybb.tistory.com/27
@Transactional에 관한 고찰 part 2
이전 글과 이어져서 작성되는 글입니다. https://cobbybb.tistory.com/25 @Transactional에 관한 고찰 (or 반성) @Transactional..??? Spring이 알아서 해주잖아..? Spring + JPA 로 개발하다 보면 눈에 보이지 않..
cobbybb.tistory.com
'JPA' 카테고리의 다른 글
QueryDSL from절 Sub Query 가능하게 하기 (3) | 2023.03.20 |
---|---|
@Transactional에 관한 고찰 part 2 (2) | 2022.08.02 |
[JPA] 일반 Join과 Fetch Join의 차이 (8) | 2021.06.30 |
Proxy형태로 동작하는 JPA @Transactional (8) | 2021.02.10 |
[JPA] OneToOne 성능 튜닝 사례 1 (1) | 2020.08.18 |