Spring Transaction으로 알아보는 AOP


이전 게시글



Transactional 과 AOP

Spring의 @Transactional은 어떻게 동작할까?

@Transactional 어노테이션은 AOP를 사용하여 구현되는데

트랜잭션의 begin과 commit을 메인 로직 앞 뒤로 수행해주는 기능을 담당한다.

@Transactional은 메서드가 실행되기 전 begin 메소드를 호출하고,

메소드가 종료된 후 commit을 호출한다.


Spring AOP는 Proxy 패턴으로 구현되는데 Spring에서 사용하는 두 가지 프록시 구현체가 있다.

springaop-process


JDK Proxy는 AOP를 적용하여 구현된 클래스의 인터페이스를 프록시 객체로 구현해서 코드를 끼워넣는 방식이다.

CGLib Proxy는 Class에 대한 Proxy생성을 지원한다. (상속을 이용)

따라서 final이나 private Method에 대한 AOP 불가능하다. (상속 된 Proxy 객체 생성 시, Override가 불가하기 때문이다.)


스프링에 대해 공부하면서 클래스를 상속하는 것에 대해서 단점을 많이 알게 되었기 때문에

“CGLib는 필요 없는게 아닐까?” 라는 생각이 드는데

그렇지 않다. 단점이 있어도 상황에 따라 최선의 선택이 될 수도 있다.



JDK Proxy

SpringBoot는 기본적으로 프록시 객체를 생성할 때 CGLib를 사용하고 있다.

그 이유는 JDK Proxy가 프록시를 생성할 때 내부적으로 Reflection을 사용하고 있기 때문이다.

리플렉션 자체가 비용이 비싼 API 이기도 하고 가급적 사용하지 않는 것을 추천하기 때문이다.

또한 JDK Proxy는 AOP 적용을 위해 반드시 인터페이스를 구현해야 한다는 단점도 있다.


Dynamic Proxy는 InvocationHandler라는 인터페이스를 구현한다. (JDK Proxy의 경우 자바에서 기본적으로 제공하고 있는 기능이다.)

InvocationHandlerinvoke 메소드를 오버라이딩하여 proxy 위임 기능을 수행하는데

이때 메소드에 대한 명세와 파라미터를 가져오는 과정에서 리플렉션을 사용한다.



CGLib

CGLib는 외부 3rd party library이며 JDK Proxy와는 달리 리플렉션을 사용하지 않고 바이트코드 조작을 통해 프록시 객체를 생성한다.

인터페이스를 구현하지 않고 해당 구현체를 상속받는 것으로 문제를 해결했기 때문에 성능상 이점도 있다.


CGLib는 Enhance 라는 클래스를 바탕으로 proxy를 생성한다.

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PostServiceImpl.class); // 프록시할 클래스 지정
enhancer.setCallback(NoOp.INSTANCE);

Object obj = enhancer.create(); // 프록시 생성 
PostServiceImpl postService = (PostServiceImpl)obj;
postService.writePost(postDTO); // 프록시를 통한 간접 접근 


기본적으로 프록시 객체들은 직접 원본 객체를 호출하기 보다는 별도의 작업을 수행하는데

CGLib의 경우 callback을 사용한다.

CGLib에서 가장 많이 사용하는 콜백은 net.sf.cglib.proxy.MethodInterceptor이다.

프록시와 원본 객체 사잉에 인터셉터를 둬서 메소드 호출을 조작하는 것을 도와줄 수 있는 것이다.

PostServiceProxyPostServiceInterceptorPostServiceImpl



참고