Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
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
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

Interface기반 Proxy - 적용 본문

인프런/[인프런] 스프링 핵심 원리 - 고급

Interface기반 Proxy - 적용

Seung__ 2021. 12. 29. 16:52

인터페이스와 구현체가 있는 V1 App에 지금까지 학습한 프록시를 도입하여 LogTrace를 사용하자.

  • 프록시를 사용하면 기존 코드를 전혀 수정하지 않고 로그 추적 기능을 도입할 수 있다.

 

1. V1 기본 클래스 의존 관계, 런타임 의존관계


 

 

2. 로그 추적용 프록시 추가


애플리케이션 실행 시점에 프록시를 사용하도록 의존관계를 설정해 주면 됨. (설정 파일을 활용)

 

 

 

3. 프록시 적용


  • OrderRepositoryProxy
import hello.proxy.app.v1.OrderRepositoryV1;
import hello.proxy.trace.TraceStatus;
import hello.proxy.trace.logtrace.LogTrace;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class OrderRepositoryInterfaceProxy implements OrderRepositoryV1 {

    private final OrderRepositoryV1 target;
    private final LogTrace logTrace;

    @Override
    public void save(String itemId) {
        TraceStatus status = null;
        try {
            status = logTrace.begin("OrderRepository.request()");
            //target 호출
            target.save(itemId);
            logTrace.end(status);

        }catch (Exception e){
            logTrace.exception(status, e);
            throw  e;
        }
    }
  • OrderServiceProxy
import hello.proxy.app.v1.OrderServiceV1;
import hello.proxy.trace.TraceStatus;
import hello.proxy.trace.logtrace.LogTrace;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class OrderServiceInterfaceProxy implements OrderServiceV1 {

    private final OrderServiceV1 target;
    private final LogTrace logTrace;

    @Override
    public void orderItem(String itemId) {
        TraceStatus status = null;
        try {
            status = logTrace.begin("OrderServiceV1.orderItem()");
            //target 호출
            target.orderItem(itemId);
            logTrace.end(status);

        }catch (Exception e){
            logTrace.exception(status, e);
            throw  e;
        }
    }
}
  • OrderController Proxy
import hello.proxy.app.v1.OrderControllerV1;
import hello.proxy.trace.TraceStatus;
import hello.proxy.trace.logtrace.LogTrace;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class OrderControllerInterfaceProxy implements OrderControllerV1 {

    private final OrderControllerV1 target;
    private final LogTrace logTrace;

    @Override
    public String request(String itemId) {
        TraceStatus status = null;
        try {
            status = logTrace.begin("OrderController.request()");
            //target 호출
            String result = target.request(itemId);
            logTrace.end(status);
            return result;

        }catch (Exception e){
            logTrace.exception(status, e);
            throw  e;
        }
    }

    @Override
    public String noLog() {
        return target.noLog();
    }
}

 

4. Configuration


  • Proxy를 사용하지 않은 Configuration은 구현체를 반환했다.
  • Proxy를 적용하기 위해 구현체가 아닌 Proxy를 반환.
import hello.proxy.app.v1.*;
import hello.proxy.config.v1_proxy.interface_proxy.OrderControllerInterfaceProxy;
import hello.proxy.config.v1_proxy.interface_proxy.OrderRepositoryInterfaceProxy;
import hello.proxy.config.v1_proxy.interface_proxy.OrderServiceInterfaceProxy;
import hello.proxy.trace.logtrace.LogTrace;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class InterfaceProxyConfig {

    //proxy를 반환. 스프링 빈에 프록시가 등록됨.

    @Bean
    public OrderControllerV1 orderController(LogTrace logTrace){
        OrderControllerV1Impl controllerImpl = new OrderControllerV1Impl(orderService(logTrace));
        return new OrderControllerInterfaceProxy(controllerImpl, logTrace);
    }

    @Bean
    public OrderServiceV1 orderService(LogTrace logTrace){
        OrderServiceV1Impl serviceImpl = new OrderServiceV1Impl(orderRepository(logTrace));
        return new OrderServiceInterfaceProxy(serviceImpl, logTrace);
    }

    @Bean
    public OrderRepositoryV1 orderRepository(LogTrace logTrace){
        OrderRepositoryV1Impl repositoryImpl = new OrderRepositoryV1Impl();
        return new OrderRepositoryInterfaceProxy(repositoryImpl, logTrace);
    }
}

 

5. V1 Proxy 런타임 객체 의존관계 설정.


  • 이제 프록시의 런타임 객체 의존 관계를 설정하면 된다. 
  • 기존에는 스프링 빈이 orderControlerV1Impl , orderServiceV1Impl 같은 실제 객체를 반환했다. 
  • 하지만 이제는 프록시를 사용해야한다.
  • 따라서 프록시를 생성하고 프록시를 실제 스프링 빈 대신 등록한다.
  • 실제 객체는 스프링 빈으로 등록하지 않는다.
  • 프록시는 내부에 실제 객체를 참조하고 있다. 
  • 예를 들어서 OrderServiceInterfaceProxy 는 내부에 실제 대상 객체인 OrderServiceV1Impl 을 가지고 있다.
  • 정리하면 다음과 같은 의존 관계를 가지고 있다.
    • proxy -> target
    • orderServiceInterfaceProxy -> orderServiceV1Impl
  • 스프링 빈으로 실제 객체 대신에 프록시 객체를 등록했기 때문에 
  • 앞으로 스프링 빈을 주입 받으면 실제 객체 대신에 프록시 객체가 주입된다.
  • 실제 객체가 스프링 빈으로 등록되지 않는다고 해서 사라지는 것은 아니다. 
  • 프록시 객체가 실제 객체를 참조하기 때문에 프록시를 통해서 실제 객체를 호출할 수 있다. 
  • 쉽게 이야기해서 프록시 객체 안에 실제 객체가 있는 것이다.

  • InterfaceProxyConfig 를 통해 프록시를 적용한 후 스프링 컨테이너에 프록시 객체가 등록된다. 
  • 스프링 컨테이너는 이제 실제 객체가 아니라 프록시 객체를 스프링 빈으로 관리한다.
  • 이제 실제 객체는 스프링 컨테이너와는 상관이 없다. 실
  • 제 객체는 프록시 객체를 통해서 참조될 뿐이다.
  • 프록시 객체는 스프링 컨테이너가 관리하고 자바 힙 메모리에도 올라간다. 
  • 반면에 실제 객체는 자바 힙 메모리에는 올라가지만 스프링 컨테이너가 관리하지는 않는다.

 

6. 실행, LogTrace Bean 등록


//@Import({AppV1Config.class, AppV2Config.class})
@Import(InterfaceProxyConfig.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProxyApplication.class, args);
    }

    @Bean
    public LogTrace logTrace(){
        return new ThreadLocalLogTrace();
    }

}

[85033b82] OrderController.request()
[85033b82] |-->OrderServiceV1.orderItem()
[85033b82] | |-->OrderRepository.request()
[85033b82] | |<--OrderRepository.request() time=1014ms
[85033b82] |<--OrderServiceV1.orderItem() time=1015ms
[85033b82] OrderController.request() time=1020ms

 

7. GitHub : 211229 Interface 기반 Proxy 적용


 

GitHub - bsh6463/Spring_Advanced_2

Contribute to bsh6463/Spring_Advanced_2 development by creating an account on GitHub.

github.com

 

Comments