Spring AOP에서 Proxy란 ?

권태용·2020년 10월 9일
2

Spring

목록 보기
1/3

이전에 트랜잭션 관련 소스코드를 확인하던 중 트랜잭션을 적용한 메소드를 클래스 내부에서 실행 할때 this.method()가 아닌 전역변수에 self = TransactinoClass()를 생성해두고 self.method()로 호출한 코드를 본적이 있다. 설명을 듣기로는 Transaction은 Proxy라는 클래스가 해당 메소드를 감싸고 있고 이를 호출 하기위해선 Proxy내부에서 호출하면 Transaction이 적용되지 않는다고 하였다.

이때 Proxy란 무엇이고 왜 Trasaction이 적용되지 않는지를 공부해 보려한다.

먼저 AOP란?

Proxy를 이해하기 앞서 AOP를 먼저 이해 해야한다. AOP(Aspect Oriented Programing)는 관점지향형 프로그래밍이다. 이는 반복 사용되는 로직들을 모듈화 하여 필요할때 호출해서 사용하는 방법이다.
Transaction이 대표적인 AOP관점이 적용되는 사례라 볼 수 있다.

쿼리를 실행하기 전에 앞서 트랜잭션을 생성하고 쿼리 실행후에 트랜잭션을 커밋하거나 롤백한 다음 트랜잭션을 닫으면 된다.

AOP를 코딩하기 위한 개념

AOP클래스를 생성하려면 아래와 같은 개념이 필요하다.

Aspect : 반복되어 사용되는 로직
Target : 적용할 로직
Advice : 반복 로직의 구현체
JointPoint, PointCut : Advice의 적용 위치

그리고 이러한 개념을 스프링의 어노테이션으로 구현 할 수 있다. 또한 AOP는 Bean에만 적용 될 수 있다.

@Before : 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행
@After : 타겟 메소드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메소드가 완료 되면 어드바이스 기능을 수행
@AfterReturning(정상적 반환 이후):타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
@AfterThrowing (예외 발생 이후) : 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행
@Around (메소드 실행 전후) : 어드바이스가 타겟 메소드를 감싸서 타겟 메소드 호출전과 후에 어드바이스 기능을 수행

출처: https://engkimbs.tistory.com/746 [새로비]

Spring AOP 동작 방식

위 에서는 Spring AOP의 사용법을 간단히 살펴봤다. 하지만 실제로 내가 작성한 AOP 코드가 실행되는 방식은 조금 다르다. https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html 이 글을 보면 Spring AOP는 dynamic proxies 또는 CGLIB를 통해 proxy라는 것을 생성한다고 한다. 여기서 프록시는 내가 작성한 AOP클래스를 핸들링 해주는 클래스이다. 그렇다면 왜 Proxy가 필요한 것 일까?

결국 AOP도 Bean이다. 객체의 역할로서만 존재하기 때문에 해당 클래스에는 Aspect와 JoinPoint들만 함수로 정의 되어 있고 Aspect와 Target을 연결해주는 역할이 없다 그렇기에 그런 역할을 Proxy 클래스가 대신해준다.

또한 위 사진을 보면 알 수 있듯이 내가 작성한 @Transactional 메소드를 호출 하면 Proxy가 먼저 호출 되고 그 다음 Transaction 코드 그리고 내가 실행시킨 메소드가 실행된다.

왜 this.save()는 @Transactional이 적용 되지 않은 걸까??

class A {
    fun caller(){
    	this.save()
    }
    
    @Transactional
    fun save(){
    }
}

fun main(){
	A().caller()
}

여기서 왜 A.caller()의 save()는 왜 트랜잭션은 적용되지 않은 걸까??

@Transactional 함수는 Proxy 외부에서 접근해야 AOP가 적용된다. 때문에 caller에서 save를 호출하면 이미 Bean의 내부에서 호출하기 때문에 Proxy를 통한 호출이 아니기 때문에 적용이 되지 않는다.

어떻게 해결 할 수 있을까?

이러한 문제를 해결 하기 위해 전역변수로 해당 Bean을 주입 받고 아래와 같이 해결 할 수도 있다.

class A {
	@Autowired
    val self:A
    
    fun caller(){
    	self.save()
    }
    
    @Transactional
    fun save(){
    }
}

혹은 서비스레이어를 두개로 나누어 처리 할 수도 있다.

class A {
	@Autowired
    val b:B
    
    fun caller(){
    	b.save()
    }
}

class B{
    @Transactional
    fun save(){
    }
}
profile
개발일기장

2개의 댓글

comment-user-thumbnail
2021년 1월 14일

메소드 요청시 기본적으로 aop proxy를 무조건 거치게 되나요?

1개의 답글