노트코드
노트코드
노트코드

블로그 메뉴

  • 홈
  • 이력서
  • 이전 블로그
  • 글쓰기
  • 관리자페이지
  • 분류 전체보기 (57)
    • 코틀린 (2)
      • 실무 프로젝트로 배우는 Kotlin & Sprin.. (2)
    • JAVA (1)
      • 디자인패턴 (1)
      • 객체지향 5대원칙 (0)
    • SPRING (32)
      • JPA (11)
      • 스프링시큐리티 (1)
      • 스프링 (8)
      • QueryDsl (1)
      • 스프링배치 (11)
    • AZURE (0)
    • ETC (10)
      • MAVEN (0)
      • GIT (0)
      • ReMind (3)
      • Exception (1)
      • CS (6)
    • 책 (8)
      • 이것이 자바다 (8)

최근 글

최근 댓글

태그

  • 스프링
  • JPA
전체 방문자
오늘
어제
hELLO · Designed By 정상우.
노트코드

노트코드

SPRING/스프링

[스프링 핵심 원리 - 고급편]인터페이스 ,클래스 기반의 프록시

2022. 2. 17. 15:17

인터페이스 기반 프록시 vs 클래스 기반 프록시

  • 인터페이스가 없어도 클래스 기반으로 프록시를 생성할 수 있다.
  • 클래스 기반 프록시는 해당 클래스에만 적용할 수 있다. 
  • 인터페이스 기반 프록시는 인터페이스만 같으면 모든곳에 적용할 수 있다.
  • 클래스 기반 프록시는 상송을 사용하기 때문에 몇가지 제약이 있다.
    • 부모 클래스의 생성자를 호출해야한다.(super())
    • 클래스에 final 키워드가 붙으면 상속이 불가능하다.
    • 메서드에 final 키워드가 붙으면 오버라이딩 할 수 없다.

 

예제 소스

각 인터페이스 코드는 아래와 같다. 

@RequestMapping//스프링은 @Controller 또는 @RequestMapping이 있어야 스프링 컨트롤러로 인식할 수 있다.
@ResponseBody
public interface OrderControllerV1 {

    @GetMapping("/v1/request")
    String request(@RequestParam("itemId") String itemId);

    @GetMapping("/v1/no-log")
    String noLog();
}

public interface OrderRepositoryV1 {
    void save(String itemId);
}

public interface OrderServiceV1 {

    void orderItem(String itemName);
}

그리고 해당 인터페이스를 구현한 코드는 아래와 같다

public class OrderControllerV1Impl implements OrderControllerV1 {

    private final OrderServiceV1 orderService;

    public OrderControllerV1Impl(OrderServiceV1 orderService) {
        this.orderService = orderService;
    }

    @Override
    public String request(String itemId) {
        orderService.orderItem(itemId);
        return "ok";
    }

    @Override
    public String noLog() {
        return "ok";
    }
}

public class OrderRepositoryV1Impl implements OrderRepositoryV1 {

    @Override
    public void save(String itemId) {
        //저장 로직
        if (itemId.equals("ex")) {
            throw new IllegalArgumentException("예외 발생");
        }
        sleep(1000);
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class OrderServiceV1Impl implements OrderServiceV1 {

    private final OrderRepositoryV1 orderRepository;

    public OrderServiceV1Impl(OrderRepositoryV1 orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Override
    public void orderItem(String itemId) {
        orderRepository.save(itemId);
    }
}

이 구조를 보면 현재 의존관계는 다음과 같다고 볼수 있다.

그리고 이 구조를 proxy패턴을 통하여 다음과 같이 변경하고자 한다.

Controller부분에 대한 Proxy코드 부분

- OrderControllerV1 인터페이스를 구현하고, 내부적으로 이미 구현되어있는 OrderControllerV1을 추가하였다.

- 실제 OrderControllerV1Impl을 실행시키되, 앞뒤로 추가 작업에 대한 코드를 작성한다.

- 나머지 interface도 동일하다.

@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()");
            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();
    }
}

그리고 설정 프록시가 동작할 수 있도록 의존관계 설정을 해줘야 하는데,

아래와 같이 작성한다.

실제 구현한 Impl을 생성하고, 그 생성한 Impl객체를 Proxy객체에 넣어주고, 해당 Proxy객체를 반환하도록 작성한다.

@Configuration
public class InterfaceProxyConfig {

    @Bean
    public OrderControllerV1 orderController(LogTrace logTrace) {
        OrderControllerV1Impl orderControllerV1 = new OrderControllerV1Impl(orderService(logTrace));
        return new OrderControllerInterfaceProxy(orderControllerV1, 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);
    }
}

 

클래스 기반의 프록시

-인터페이스 기반의 프록시와 큰 틀에서 비슷하지만 , 클래스 기반의 프록시는 인터페이스가 아니기때문에, 해당 프록시를 상속받아서 만들어야 한다는 차이점이 있다.

- 아래와 같은 형태의 구조를 가진다.

중요한점은 implements가 아닌 extends를 한다는점이 중요!(interface가 아니기 때문)

public class OrderControllerConcreteProxy extends OrderControllerV2 {

    private final OrderControllerV2 target;
    private final LogTrace logTrace;

    public OrderControllerConcreteProxy(OrderControllerV2 target, LogTrace logTrace) {
        super(null);
        this.target = target;
        this.logTrace = logTrace;
    }

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

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

public class OrderRepositoryConcreteProxy extends OrderRepositoryV2 {

    private final OrderRepositoryV2 target;
    private final LogTrace logTrace;

    public OrderRepositoryConcreteProxy(OrderRepositoryV2 orderRepositoryV2, LogTrace logTrace) {
        this.target = orderRepositoryV2;
        this.logTrace = logTrace;
    }

    @Override
    public void save(String itemId) {

        TraceStatus status = null;
        try {
            status = logTrace.begin("OrderRepository.request()");
            target.save(itemId);
            logTrace.end(status);
        } catch (Exception e) {
            logTrace.exception(status, e);
            throw e;
        }

    }

}

public class OrderServiceConcreteProxy extends OrderServiceV2 {

    private final OrderServiceV2 target;
    private final LogTrace logTrace;

    public OrderServiceConcreteProxy(OrderServiceV2 target, LogTrace logTrace) {
        super(null);
        this.target = target;
        this.logTrace = logTrace;
    }
}

그리고 아래와 같이 빈등록을 해준다.

인터페이스 프록시와 동일하다. 기존 구현체를 생성하여, 프록시에 DI해준다.

@Configuration
public class ConcreteProxyConfig {

    @Bean
    public OrderControllerV2 orderControllerV2(LogTrace logTrace) {
        OrderControllerV2 orderController = new OrderControllerV2(orderServiceV2(logTrace));
        return new OrderControllerConcreteProxy(orderController, logTrace);

    }

    @Bean
    public OrderServiceV2 orderServiceV2(LogTrace logTrace) {
        OrderServiceV2 serviceImpl = new OrderServiceV2(orderRepositoryV2(logTrace));
        return new OrderServiceConcreteProxy(serviceImpl, logTrace);
    }

    @Bean
    public OrderRepositoryV2 orderRepositoryV2(LogTrace logTrace) {

        OrderRepositoryV2 orderRepositoryV2 = new OrderRepositoryV2();
        return new OrderRepositoryConcreteProxy(orderRepositoryV2, logTrace);

    }
}

 

'SPRING > 스프링' 카테고리의 다른 글

[스프링 핵심 원리 - 고급편] 프록시, 프록시 패턴, 데코레이터 패턴  (0) 2022.02.16
[스프링 핵심 원리-고급] 템플릿 콜백 패턴  (0) 2022.01.27
[스프링 핵심 원리 -고급] 전략패턴  (0) 2022.01.27
[스프링 핵심 원리-고급] 템플릿 메서드 패턴  (0) 2022.01.25
[스프링 핵심 원리-고급] 쓰레드 로컬 주의사항  (0) 2022.01.22
    'SPRING/스프링' 카테고리의 다른 글
    • [스프링 핵심 원리 - 고급편] 프록시, 프록시 패턴, 데코레이터 패턴
    • [스프링 핵심 원리-고급] 템플릿 콜백 패턴
    • [스프링 핵심 원리 -고급] 전략패턴
    • [스프링 핵심 원리-고급] 템플릿 메서드 패턴
    노트코드
    노트코드
    노션 블로그에서 티스토리로 이전공사중

    티스토리툴바