1. 자동 프록시 생성
중복 문제 제거하기
- 매번 ProxyFactoryBean 들을 복사하여 등록해야하는 문제 (실수하기 쉽다.)
해결법 1. 빈 후처리기 (BeanPostProcessor) 를 이용한 자동 프록시 생성기
- BeanPostProcessor 인터페이스를 구현 (DefaultAdvisorAutoProxyCreator) 해서 만드는 빈 후처리기
- DefaultAdvisorAutoProxyCreator (Advisor를 이용한 자동 프록시 생성기) 를 이용해보자.
사용법
// Advice 등록
@Bean
public TransactionAdvice transactionAdvice() {
TransactionAdvice transactionAdvice = new TransactionAdvice();
transactionAdvice.setTransactionManager(transactionManager());
return transactionAdvice;
}
// 어떤 클래스타입의 경우에 적용할 지 포인트컷을 설정한다.
@Bean
public NameMatchClassMethodPointcut transactionPointCut() {
NameMatchClassMethodPointcut pointcut = new NameMatchClassMethodPointcut();
pointcut.setMappedName("upgrade*");
pointcut.setMappedClassName("*ServiceImpl");
return pointcut;
}
// Advisor 등록
@Bean
public DefaultPointcutAdvisor transactionAdvisor() {
return new DefaultPointcutAdvisor(transactionPointCut(), transactionAdvice());
}
// 빈 후처리기를 빈으로 등록하면, 자동으로 Advisor를 호출하여 pointcut에 부합한다면 advice 호출
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
포인트컷은 메소드만 검증하는 NameMatchMethodPointcut을 상속한 뒤, 필터를 추가한 클래스를 사용하였다.
public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut {
public void setMappedClassName(String mappedClassName) {
this.setClassFilter(new SimpleClassFilter(mappedClassName));
}
static class SimpleClassFilter implements ClassFilter {
private String mappedName;
private SimpleClassFilter(String mappedName) {
this.mappedName = mappedName;
}
@Override
public boolean matches(Class<?> clazz) {
return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName());
}
}
}
2. 포인트컷 표현식
AspectJExpressionPointcut 클래스를 사용한다.
표현식
- execution([접근제한자 패턴] 리턴타입패턴 [풀패키지네임.] 메소드이름 (파라미터 타입) [throws 예외])
설명 | 생략가능여부 | |
접근제한자 패턴 | private / public / protected 등 접근제한자 | 생략가능 |
리턴타입 패턴 | 해당 함수의 리턴타입 *(asterisk) 을 사용해서 모든 타입이 가능하다고 명시할 수 있다. |
생략 불가능 |
풀패키지네임. | 클래스의 풀 패키지네임을 명시한다. 생략하면 모든 것을 허용하겠다는 뜻이다. (.) -> 구분자 (*) -> 패키지 이름이나 인터페이스 이름에 *를 사용하여 뒤에 어떤이름이든 올 수 있도록 함 (..) -> 특정 패키지의 서브패키지를 모두 포함할 수 있다. |
생략가능 |
메소드이름 | 메소드 이름 패턴이다. 모든 메소드를 다 가능하게 하려면 (*) 을 사용한다. |
생략 불가능 |
파라미터 타입 | 파라미터가 없는 메소드를 지정하고 싶다면 () 를 사용한다. (..) -> 모든 파라미터 허용 (...) -> 뒷부분의 파라미터 조건 생략 가능 |
생략 불가능 |
throw 예외 | 예외 이름 패턴 | 생략 가능 |
예시)
- execution(* minus(int, int))
- 모든 리턴타입을 허용 / minus(int, int) 함수에 대한 포인트컷을 설정한다.
- execution(* minus(..))
- 모든 리턴타입 허용 / minus(파라미터 자유) 에 대한 포인트컷을 설정한다.
- execution(* *(..))
- 모든 메소드를 허용하는 포인트컷
execution 이외에도..
- bean(*Service)
- Service로 끝나는 모든 빈에 대한 포인트 컷 설정
- @annotation(org.springframework.transaction.annotation.Transactional)
- @Transactional 어노테이션이 적용된 메소드에 대한 포인트 컷 설정
3. AOP란?
- 부가적인 기능을 분리해서 Aspect 라는 독립적인 모듈로 만들어서 설계하고 개발하는 방식
적용 기술
- Proxy 를 이용한 AOP
- 지금까지 위에서 한 것 (Runtime weaving) / ProxiedFactoryBean
- 바이트코드 생성과 조작을 통한 AOP (AspectJ)
- 컴파일된 TargetClass 파일을 수정하거나 / 바이트코드를 조작한다.
스프링의 Proxy 방식 AOP 를 사용하려면 최소 4개의 빈을 등록해야한다.
- 자동 프록시 생성기
- BeanPostProcessor
- 어드바이스
- 토비의 스프링 책에서는 MethodInterceptor를 구현한 TransactionAdvice 사용
- 포인트컷
- 토비의 스프링 책에서는 AspectJExpressionPointcut을 사용
- 어드바이저
- 토비의 스프링 책에서는 DefaultPointcutAdvisor를 등록해서 사용
- 자동 프록시 생성기에 의해서 자동으로 검색되어 사용된다.
4. 트랜잭션 속성
현재 TransactionAdvice 코드의 일부분
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
Object ret = invocation.proceed();
transactionManager.commit(status);
return ret;
} catch (RuntimeException e) {
transactionManager.rollback(status);
throw e;
}
DefaultTransactionDefinition은 무엇인가?
- TransactionDefinition Interface를 구현하는 구현체이며, 트랜잭션 동작방식에 영향을주는 네가지 속성을 정의한다.
1. 트랜잭션 전파
- 트랜잭션 경계에서 이미 진행중인 트랜잭션이 있을 때, 없을 때 어떻게 동작할 것인지를 결정하는 방식이다.
PROPAGATION_REQUIRED | 진행중인 트랜잭션이 없으면 새로 시작하며, 진행중인 트랜잭션이 있다면 해당 트랜잭션에 참여한다. DefaultTransactionDefinition.class의 트랜잭션 전파 레벨은 PROPAGATION_REQUIRED 이다. |
PROPAGATION_REQUIRES_NEW | 항상 새로운 트랜잭션을 시작한다. 항상 새로운 트랜잭션을 만들어서 독립적으로 동작하게 한다. |
PROPAGATION_NOT_SUPPORTED | 트랜잭션없이 동작하도록 설정한다. 진행중인 트랜잭션이 있더라도 무시한다. |
2. 격리수준
- 기본적으로 데이터베이스에 Isolation Level 이 설정되어있지만, 트랜잭션 단위로 격리수준을 조정할 수 있다.
- DefaultTransactionDefinition.class 의 격리수준은 ISOLATION_DEFAULT (DataSource 에 설정된 기본 격리수준을 따름)
3. 제한시간
- 트랜잭션을 수행하는 제한시간을 설정할 수 있다.
- DefaultTransactionDefition.class 의 기본 설정은 제한시간이 없는 것이다.
4. 읽기전용
- read-only 로 설정하면 트랜잭션 내에서 데이터 write 의 시도를 막을 수 있다.
DefaultTransactionDefinition.class 는 기본적인 옵션이 이미 적용되어있지만, 함수마다 다른 트랜잭션 속성을 적용시키고 싶다면?
- MethodInterceptor => TransactionInterceptor을 이용 (Methodinterceptor + 메소드 이름패턴 제공)
TransactionInterceptor
프로퍼티 | 1. TransactionManager : 어떤 TransactionManager로 트랜잭션을 수행할 것인지 2. Properties : 메소드이름 패턴에대해서 어떤 Transaction 속성을 줄 것인지 |
예외상황 | RuntimeException : 롤백 CheckedException : 롤백하지 않음 rollbackOn 속성을 통해서 기능을 추가할 수 있다. |
이름패턴을 이용한 속성 지정 |
"PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_????,-Exception1,+Exception2" -Exception : 롤백 대상에 추가 +Exception : 롤백 대상에서 제거 |
TransactionInterceptor 로 등록한 Bean
@Bean
public TransactionInterceptor transactionAdvice() {
Properties properties = new Properties();
properties.put("get*", "PROPAGATION_REQUIRED,readOnly,timeout_30");
properties.put("upgrade*", "PROPAGATION_REQUIRES_NEW,ISOLATION_SERIALIZABLE");
properties.put("*", "PROPAGATION_REQUIRED");
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionManager(transactionManager());
transactionInterceptor.setTransactionAttributes(properties);
return transactionInterceptor;
}
프록시 방식 AOP 로 생성된 프록시에서 타깃 오브젝트 내의 메소드를 호출하는 경우에는 적용되지 않는다. (mommoo.tistory.com/92)
- 위의 글은 바이트코드로 된 설명이나, 프록시의 경우에도 같은 방식으로 적용이 되지 않는다.
5. 트랜잭션 어노테이션
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
}
@Target
- Type(클래스, 인터페이스) / Method(메소드) 에 사용할 수 있는 어노테이션이다.
@Retention
- 런타임 시점까지 유지되므로, 리플렉션을 통해서 정보를 얻을 수 있다.
@Inherited
- 상속을 통해서도 어노테이션 정보를 얻을 수 있다.
스프링은
- Pointcut : @Transactional 어노테이션이 붙은 오브젝트를 타깃 오브젝트로 인식한다. (TransactionAttributeSourcePointcut)
- Advice : AnnotationTransactionAttributeSource 를 통해서 어노테이션에 속성정보를 참조한다.
'책 정리 > 토비의 스프링' 카테고리의 다른 글
8. 스프링이란 무엇인가 (0) | 2021.02.20 |
---|---|
7. 스프링 핵심 기술의 응용 (0) | 2021.02.14 |
6.1. AOP (0) | 2021.02.02 |
5. 서비스 추상화 (0) | 2021.02.01 |
4. 예외 (0) | 2021.01.30 |