티스토리 뷰

클래스 및 메서드에 Proxy 기반으로 동작하는 어노테이션을 선언해주면 해당 클래스의 메서드에 부가 기능이 추가되어 실행됩니다.  대표적으로 @Transactional이 있습니다.

 

이번 글에서는 Proxy 기반으로 동작하는 어노테이션을 AspectJ 모듈과 함께 사용할 경우 주의할 점에 대해 알아보겠습니다. 예제코드를 함께 살펴보겠습니다.

 

예제코드

위 코드는 DB에서 데이터를 조회한 후 JPA Dirty Checking으로 Entity의 상태를 변경하는 단순한 코드입니다.

 

 

update 쿼리가 정상적으로 실행된 걸 확인할 수 있습니다.

이 쿼리가 실행되는 이유는 JPA의 영속성 컨텍스트의 스코프는 기본적으로 @Transactional이 선언된 메서드가 호출되어 종료될 때까지 유효합니다. 해당 Entity가 영속성 컨텍스트에 존재하면 Dirty Checking으로 상태를 변경하여 변경한 데이터로 update 쿼리가 실행됩니다. 즉, @Transactional로 인해 Proxy가 감싸서 처리해주었음을 의미합니다.

 

 

이번에는 코드를 위 사진처럼 변경해보겠습니다. @Transactional의 위치를 변경하였고 test() 메서드가 호출되기 전에 실행되는 before() 메서드 위에다 @Transactional을 선언하였습니다.

 

 

아까와는 다르게 update 쿼리가 실행되지 않았습니다.

Pointcut으로 호출될 메서드를 지정하고 해당 메서드가 호출되기 전에 before() 메서드가 먼저 호출되어도 Pointcut으로 지정된 test() 메서드에는 @Transactional이 없어서 Proxy로 감싸지 않고 실제 target class의 메서드가 동작하는 것 같습니다

 

빨간색 표시해둔 부분은 제가 추측한 내용입니다. 그래서 왜 @Transactional이 동작이 되지 않는지 문서를 살펴보기 시작했습니다.

 

 

Spring AOP 공식문서에 다음과 같은 내용이 있습니다. 

 

 

Spring AOP는 AspectJ와 동일한 우선순위 규칙이 있으며 이 우선순위는 @Order 또는 Ordered 인터페이스를 구현해서 우선순위를 지정해줄 수 있고 더 낮은 값이 높은 우선순위를 갖는다고 합니다.

 

 

@Transactional의 순서는 Ordered.LOWEST_PRECEDENCE입니다. 이 값은 Intger.MAX_VALUE를 의미합니다.

 

 

동일한 조인포인트에서 실행될 경우 실행순서가 보장되지 않으므로 AspectJ Bean은 Order를 1로 세팅해보겠습니다.

 

 

결과는 마찬가지로 update 쿼리가 실행되지는 않았습니다.

 

 

아까 제가 위에도 첨부했던 사진인데 마지막 문구를 보면 왜 안 되는지 알 수 있습니다.

 

 

동일한 관점에서 정의된 두 개의 Advice들은 모두 동일한 조인포인트에서 실행되어야 하는 경우 순서는 정의되지 않는다고 합니다. 리플렉션을 통해 선언 순서를 검색할 방법이 없기 때문이라고 합니다.

 

이러한 접근방식보다는 위 설명처럼 별도의 관점 클래스로 분리하여 리팩터링 하라고 권장하고 있습니다.

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/03   »
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
글 보관함