안녕하세요.
이번 글에서는 자바의 어노테이션에 대해서 분석한 내용을 정리해보고자 합니다. 순서는 아래와 같습니다.
- Annotation의 동작 및 속성
- @Transactional이 인식되는 과정
어노테이션의 기본 속성 및 주요 예시인 @Transactional 어노테이션의 인식 과정에 대해서 정리하였습니다.
1. Annotation의 동작 및 속성
어노테이션은 클래스, 생성자, 메소드 등 특정 위치에 정의될 수 있으며, 정책에 따라서 컴파일 타임에 적용되거나 리플렉션 활용하여 런타임에 접근될 수 있습니다. 예시는 아래와 같습니다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration
- Retention 정책
어노테이션을 어디까지 유지할 것인지를 결정하는 속성입니다. 어노테이션의 @Retention을 통해 지정할 수 있습니다.
- RetentionPolicy.SOURCE : 컴파일 시점에 사용된 후 어노테이션은 제거됩니다.
- RetentionPolicy.CLASS : 컴파일 후 클래스 파일까지 어노테이션이 포함되나, 런타임에는 활용할 수 없습니다.
- RetentionPolicy.RUNTIME : 런타임에도 리플렉션을 통해서 접근이 가능합니다.
- 위치 정책
어노테이션을 어느 위치에 적용할 수 있는지를 결정하는 속성입니다. @Target을 통해서 지정할 수 있습니다. 예를 들어, 타입(클래스, 인터페이스, Enum), 필드, 메소드, 파라미터, 생성자 등에 적용할 수 있도록 지정이 가능합니다.
2. @Transactional이 인식되는 과정
어노테이션이 어떻게 활용되는지 파악하기 위해 대표적인 어노테이션 @Transactional이 인식되는 방식에 대해서 확인했습니다. 아래 클래스를 확인해보면, 트랜잭션 관련 클래스 및 메소드는 AOP를 활용하여 Advisor로 등록되고 있음을 확인했습니다.
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder((Integer)this.enableTx.getNumber("order"));
}
return advisor;
}
}
- Pointcut : @Transactional이 적용된 메소드 또는 클래스를 찾음
- Advice : 트랜잭션 관련 정책을 적용함
위 소스만을 놓고 보았을 때, TransactionAttributeSource가 내부적으로 Pointcut 처럼 동작하여 @Transactional 어노테이션이 붙은 메소드 및 클래스를 찾고 transactionInterceptor가 advice로 동작하여 트랜잭션 정책을 관리할 것으로 짐작되었습니다.
예상과 동일하게 BeanFactoryTransactionAttributeSourceAdvisor 객체 내 Pointcut 필드가 @Trasactional이 붙어 있는 메소드 또는 클래스와 그것의 속성(isolation, progatation 등)을 조회하고 있었습니다.
class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();
// ...
}
class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
public boolean matches(Method method, Class<?> targetClass) {
return this.transactionAttributeSource == null || this.transactionAttributeSource.getTransactionAttribute(method, targetClass) != null;
}
}
class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
for(TransactionAnnotationParser parser : this.annotationParsers) {
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
}
class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);
return attributes != null ? this.parseTransactionAnnotation(attributes) : null;
}
}
'기술 블로그' 카테고리의 다른 글
@Transactional Deep Dive (1) | 2025.02.19 |
---|---|
Java ObjectMapper (Feat. RedisTemplate) (1) | 2025.02.13 |
스프링 Kafka Consumer Deep Dive - 1 (1) | 2025.02.05 |
Spring Security에서의 Filter (0) | 2024.12.17 |
필터와 인터셉터란? (0) | 2024.12.11 |