-
[Spring] 프록시 패턴과 데코레이터 패턴 - 3Spring 2022. 6. 16. 16:57
인터페이스 기반 프록시를 적용한다면
이전까지 정리했던 내용을 다시 한번 확인하자면
'클라이언트 -> 프록시 -> 서버'로 의존관계가 이루어져야한다.
이를 웹 서버로 바라보자면
클라이언트 -> 프록시 컨트롤러 -> 컨트롤러 -> 프록시 서비스 -> 서비스
-> 프록시 리포지토리 -> 리포지토리
순으로 요청이 흘러가고 응답은 이의 역순으로 흘러간다는 것을 알 수 있다.
@Configuration public class InterfaceProxyConfig { // ControllerInterface : 컨트롤러 인터페이스 // ControllerImpl : 컨트롤러 구현체 // ControllerProxy : 컨트롤러 프록시 @Bean public ControllerInterface controller() { ControllerImpl impl = new ControllerImpl(service()); // 컨트롤러 구현체 객체참조를 가지고 있는 프록시를 반환한다. return new ControllerProxy(impl); } // service()는 현재 생략했지만 컨트롤러를 작성했듯 서비스, 리포지토리에 대해서 // 프록시를 호출하도록 한 것이다. }
위와 같이 스프링 빈을 등록할 때 주의해야할 점은 실제 객체를 스프링 빈으로 등록하지 않고
프록시를 스프링 빈으로 등록해야한다는 점이다.
따라서 실제 객체 대신 프록시 객체가 대신 주입되도록 하는 것이다.
또한 프록시 객체가 실제 객체를 잠조하고 있기 때문에 프록시를 통해서 실제 객체를 호출할 수 있다.
(스프링 컨테이너에는 올라가지 않지만 자바 힙 메모리에는 올라간다.)
구체 클래스 기반 프록시 작성
항상 인터페이스를 작성하지는 않기 때문에 구체 클래스에도 프록시를 적용해야하는 경우가 있다.
이럴 때에는 어떻게 프록시를 적용해야할까
자바의 다형성은 인터페이스를 구현하든, 클래스를 상속하든 상위 타입만 맞으면 다형성이 적용된다.
따라서 서비스, 리포지토리 클래스를 상속받아서 프록시를 만들면 기존 서비스, 리포지토리
객체 대신 프록시를 주입할 수 있게 된다.
이때 중요한 점은 프록시의 생성자에 super(null)를 작성해줘야한다.
왜냐하면 각 객체들은 프록시 또는 실제 객체를 잠조하기 위한 필드를 가지고 있고
이를 위한 생성자를 가지고 있기 때문에 기본생성자를 만들지 않았기 때문이다.
또한 이때 null을 넣는 이유는 프록시는 프록시의 기능(접근 제어, 부가기능)을 동작하든
실제 객체를 호출하기 때문에 기능을 상속받아 사용하지 않기 때문이다.
인터페이스 기반 프록시와 클래스 기반 프록시
인터페이스가 없어도 클래스 기반으로 프록시를 생성할 수 있었는데
클래스 기반 프록시는 해당 클래스에만 적용할 수 있던 반면 인터페이스 기반 프록시는
인터페이스만 같으면 모든 곳에 적용할 수 있었다.
또한 클래스 기반 프록시는 상속을 사용하기 때문에
부모 클래스의 생성자를 호출하거나, 클래스에 final 키워드가 붙을 시 상속이 불가능하고
메서드에 final 키워드가 붙는다면 해당 메서드를 오버라이딩 불가능한 문제가 있다.
결국 인터페이스 기반 프록시는 상속이라는 제약에서 자유롭고 역할과 구현을 명확하게 나눈다.
지금까지 프록시를 통해 원본 코드를 변경하지 않고 기능을 추가하는 방법을 알아보았다.
하지만 프록시를 사용한다해도 적용하는 클래스의 수만큼 프록시 클래스를
만들어야하기 때문에 부족한 점이 있다.
이를 해결하기 우해서 동적 프록시 기술을 사용한다.
[참고 : 인프런 - 스프링 핵심 원리 - 고급편]
'Spring' 카테고리의 다른 글
[Spring] 동적 프록시 기술 - JDK 동적 프록시 (0) 2022.06.16 [Spring] 동적 프록시 기술 - 리플렉션 (0) 2022.06.16 [Spring] 프록시 패턴과 데코레이터 패턴 - 2 (0) 2022.06.16 [Spring] 프록시 패턴과 데코레이터 패턴 - 1 (0) 2022.06.16 [Spring] 전략 패턴 - 상속이 아닌 위임을 활용 (0) 2022.06.15