- 공통 비즈니스 로직 분리(제휴사 인터페이스 통합 및 클래스 설계)

- Hystrix with Spring ( Circuit Breaker ) 를 사용할 때 생각해 보아야 할 점

- Spring annotation이 동작하는 원리 : AOP

- 두 트랜잭션이 동시에 실행되는 것을 막기(isolation, deadlock, Exclusive lock)

- RestTemplate 사용 시 ResponseType으로 generic 타입을 사용할 때 발생하는 문제


 

스프링에는 달아 놓기만 하면 알아서 동작하는 많은 어노테이션들이 있다.

런타임에 이런 어노테이션이 동작하려면, 원래는 어노테이션을 적용하기 위한 별도의 proxy 함수가 필요하다.

```

- 별도의 proxy 함수를 호출

-- 리플렉션으로 어노테이션 체크

-- 어노테이션 동작 - 실제 함수 호출 ( 어노테이션에 따라 시점은 다를 수 있음. )

```

그러나 스프링에서는 이런 별도의 proxy 함수를 호출하지 않아도 annotation이 동작한다.

이게 가능한 이유는 AOP proxy가 동작하기 때문이다.

 

스프링 컨테이너는 어떤 Bean을 주입할 때 proxy로 감싼 aspect-aware bean을 주입해준다.

따라서 어떤 개체가 DI 받게되는 bean은 aspect-aware bean이고, 이를 통해 메서드를 호출하면 실은 proxy 함수가 호출된다. (이게 스프링에서 AOP를 구현하는 방식이다.)

따라서 별도의 proxy 함수를 직접 불러주지 않아도, @어노테이션을 달아 놓는 것 만으로도 동작하게 된다.

이런 식으로 공통 기능을 별도로 분리해내서 핵심 기능 작성에만 집중할 수 있도록 하는 방식을 AOP라고 하며, 스프링의 AOP는 proxy 기반으로 동작한다.

 

internal use method에 @어노테이션을 붙여도 동작하지 않는 현상

아래는 @HystrixCommand를 예로 들어 설명했지만, @Transactional 같은 모든 어노테이션에도 적용 되는 얘기다.

```java

public class PartnerApiClient {

    public UserInfo getUserInfo(String userCi) {

        this._getUserInfo(userCi);

    }

 

    @HystrixCommand(fallbackMethod = "fallback")

    private UserMileageInfoResponseWrapper _getUserInfo(String userCi)  {...}

 

    private UserMileageInfoResponseWrapper fallback(String userCi) {... }

}

```

어노테이션에 따르면 분명 _getUserInfo에서 실패하면 fallback 메서드로 빠져야 하는데 그렇지 않다. 왜일까?

_getUserInfo가 private이라? 아니다. 

이는 Spring의 AOP 지원이 프록시 기반이라는 점에서 기인한다. 

The general idea is that spring AOP is proxy-based, i.e. it assumes that when the bean is used as a dependency and its method(s) should be advised by particular aspect(s) the container injects aspect-aware bean proxy instead of the bean itself. 

https://github.com/Netflix/Hystrix/issues/1020#issuecomment-199416640

http://blog.harmonysoft.tech/2009/07/spring-aop-top-problem-1-aspects-are.html

 

위에서 어노테이션이 동작하려면 aspect-aware bean이어야 하며, 스프링 컨테이너는 어떤 Bean을 DI할 때 메서드들을 proxy 함수로 wrapping한 aspect-aware bean을 주입해준다고 했다.

 

반면 ``java this._getUserInfo()`` 처럼 Bean 내부에서 자기 자신의 메서드를 호출하게 되면, 컨테이너로부터 DI받은 Bean을 통해 메서드를 호출하는게 아니라 proxy로 감싸져 있지 않은 자기 자신 메서드를 호출하는 것이다. 즉, aspect-aware bean이 아니라 pure-bean의 메서드를 호출하게 되는 것. 그래서 AOP가 동작하지 않아 @HystrixCommand가 동작하지 않는다.

 

해결 방법은 4가지. 

1. self-call 하지 않도록 리팩토링 하는 방법.

 

2. replace self-calls with aspect-aware proxy calls, i.e. use the statement like 

```java

((PartnerApiClient)AopContext.currentProxy())._getUserInfo(userCi);

 

@EnableAspectJAutoProxy(exposeProxy = true) // Application 클래스에 이 것도 추가해주어야 함.

```

이 방법은 AopContext라는 API를 사용하면서 프레임워크와 결합이 생긴다.

 

3. use aspectj weaving ( 별도의 API가 비즈니스 로직에 들어가지 않으니 프레임워크 결합 없이 처리할 수 있어 추천하는 방법 ) 

https://minwan1.github.io/2017/10/29/2017-10-29-Spring-Transaction,AspectJ-Compile/

 

4. 아예 @어노테이션을 안쓰고 Programmatic한 방법으로 직접 API를 불러 사용하는 방법.

예를 들면 @Transactional 대신 PlatformTransactionManager를 직접 DI 받아서 사용한다던가.

단, 2번 처럼 프레임워크 API를 사용하면서 생기는 프레임워크 의존성에 대해서는 생각해보아야 함.

 

참고) Spring에서 annotation은 interface로 부터는 상속되지 않고, abstract class로부터는 상속된다.

https://stackoverflow.com/questions/18585374/spring-aop-inherited-annotation-from-an-interface

물론 class에서 상속 받는 것도 @Inherited 메타 어노테이션이 붙어 있는 어노테이션이어야 가능하다.

 

이런 어노테이션 상속은, 프레임워크에서 해당 메서드(필드)에 달려있는 어노테이션을 어디까지 검색할 것인지에 달려있으므로 프레임워크 마다 다를 수 있다.

 

 

+ Recent posts