- Spring에서 프록시 사용 12022년 11월 05일
- starryeye
- 작성자
- 2022.11.05.:03
이전 포스팅에서 프록시 개념을 사용한..
프록시 패턴과 데코레이터 패턴에 대해 알아보았다..
프록시 개념 : https://starryeye.tistory.com/106
프록시 패턴 : https://starryeye.tistory.com/107
데코레이터 패턴 : https://starryeye.tistory.com/108
이제 실제로 스프링 프레임워크에서 스프링 컨테이너에 프록시를 도입할 경우의...
3가지 상황별로 알아 보자..
1. 등록될 스프링 빈의 인터페이스가 있고.. 그 구현체가 수동 등록되어 있을 경우
2. 등록될 스프링 빈의 인터페이스가 없고.. 그냥 그 객체 자체가 수동 등록 되어 있을 경우
3. 컴포넌트 스캔을 사용하여 어노테이션 기반으로 스프링 빈이 자동 등록 될 경우
두 가지(1, 2번)만 먼저 알아보자..
1. 등록될 스프링 빈의 인터페이스가 있고.. 그 구현체가 수동 등록되어 있을 경우
이 경우.. 간단하다.
그냥 해당 인터페이스를 구현한 프록시 객체를 만들고..
수동 등록 과정에서...
프록시 객체는 실제 객체를 주입받도록 하고..
스프링 빈으로 프록시 객체를 반환 하도록 한다.
클래스 의존관계는 다음과 같다.
여기서 Proxy가 붙은 것들은.. 프록시를 도입하기 위해 새로 만든 클래스이며..
그외는 원래 있던 클래스 이다.(예시 조건)
LogTrace는 프록시의 부가기능을 수행하기 위해 있는 클래스이다.
각 프록시 클래스에 의존관계 주입이 된다. (생성자 주입으로 수행되는 것으로 구현)
코드로 자세 하게 알아보자..
@Configuration public class Config { @Bean public Controller controller(LogTrace logTrace) { RealController realController = new RealController(service(logTrace)); return new ProxyController(realController, logTrace); } @Bean public Service service(LogTrace logTrace) { RealService realService = new RealService(repository(logTrace)); return new ProxyService(realService, logTrace); } @Bean public Repository repository(LogTrace logTrace) { RealRepository realRepository = new RealRepository(); return new ProxyRepository(realRepository, logTrace); } }
LogTrace는 따로 스프링 빈으로 등록되어 있다고 생각하자.
빈 등록 과정에서 프록시를 생성하여 스프링 빈으로 등록하고 있고..
프록시에는 실제 타겟 객체가 주입되고 있다..
따라서..
RealController에서 Service를 호출 해줄 때...
스프링 컨테이너의 빈 등록과정에서..
RealController에 주입된 Service는 ProxyService이므로..
RealController -> ProxyService -> RealService 와 같이 호출 될 것이다.
따라서..
어떤 객체에서 주입받은 Controller를 호출할 때도..
Service를 호출할 때도.. Repository를 호출할 때도..
모두 프록시가 먼저 호출되고..
프록시가 실제 객체를 호출 하게 되도록 한 것이다.
2. 등록될 스프링 빈의 인터페이스가 없고.. 그냥 그 객체 자체가 수동 등록 되어 있을 경우
이 경우에도 인터페이스가 있는 것 처럼 프록시 클래스를 만들어 주면 된다.
사실 다형성이란.. 인터페이스로 수행해도 되지만..
실제 구체 클래스를 상속한 프록시 클래스를 만들어서
실제 구체 클래스를 인터페이스 처럼? 사용해도 된다.
클래스 의존 관계는 다음과 같다.
LogTrace는 1번 상황과 동일하다.
주의점은..
의존관계는 타입으로 그리기 때문에..
SpringContainer(ApplicationContext)이 실제 객체를 가지고 있는 것 처럼 보이지만..
런타임시.. 프록시 객체들을 빈으로 가지고 있다.(스프링 빈 등록)
(이는.. 실제 객체가 프록시 객체를 주입받는다는 점과도 동일하다.)
코드로 자세히 알아보자.
@Configuration public class Config { @Bean public RealController realController(LogTrace logTrace) { RealController realController = new RealController(realService(logTrace)); return new ProxyController(realController, logTrace); } @Bean public RealService realService(LogTrace logTrace) { RealService realService = new RealService(realRepository(logTrace)); return new ProxyService(realService, logTrace); } @Bean public RealRepository realRepository(LogTrace logTrace) { RealRepository realRepository = new RealRepository(); return new ProxyRepository(realRepository, logTrace); } }
실제 객체 타입으로 스프링 빈을 등록하고 있지만...
스프링 빈으로 등록되는 객체 반환 값은 실제 객체를 상속한 프록시 객체임을 알 수 있다.
프록시 객체에는 실제 객체가 주입되고 있다.
실제 객체에 주입되는 객체는...
스프링 컨테이너에 의해 스프링 빈으로 등록된 프록시가 주입 될 것이다.
따라서..
런타임 호출 순서는
1번과 동일하게
프록시 -> 실제 객체 -> 프록시 순으로 진행 될 것이고..
위 스프링 빈들을 주입 받은 어떤 클래스가 스프링 빈으로 호출하면..
프록시가 먼저 호출될 것이다.
<주의점>
인터페이스를 사용한 1번과 다르게
2번은 실제 객체를 상속받아 프록시 객체를 생성하기 때문에 제약사항이 있다.
(Java 문법 제약)
1. 부모 클래스의 생성자를 호출해야 한다.
-> 예를 들어 알아보자..
-> 부모 클래스가 될 실제 객체에 생성자 자체가 없다면..
자바는 파라미터가 없는 기본 생성자를 자체적으로 만들어준다.
자식 클래스에서는 부모클래스의 생성자를 반드시 호출 해줘야 하는데..
부모 클래스의 생성자가 없으며..
자식 클래스 생성자에서 부모클래스 생성자 호출을 생략해도..
super(); 를 호출 한 것으로 동작된다.
-> 부모 클래스가 될 실제 객체에 생성자가 있는데... 파라미터를 받는 생성자가 있다면..
이 경우.. 기본 생성자가 없으므로..
자식 클래스 생성자에서는 파라미터를 null로 전달하는..
super(null); 로 호출 해줘야한다.
(프록시 기능만 자식 클래스에서 사용할 것이고 실제객체는 파라미터로 따로 주입받는다.)
2. 클래스에 final 키워드가 붙으면 상속이 불가능하다.
3. 메서드에 final 키워드가 붙으면 메서드 오버라이딩이 불가능 하다.
1번, 2번 문제점..
프록시를 적용하고 싶은 실제 클래스의 프록시 클래스를 구현하는 것 자체가
중복이며 너무 많이 만들어 줘야한다..
-> 다음 포스팅인 동적 프록시로 해결 해보자..
'Spring > Core' 카테고리의 다른 글
Spring에서 프록시 사용 3 (0) 2022.11.14 Spring에서 프록시 사용 2 (0) 2022.11.14 Spring에서 자주 쓰이는 디자인 패턴 5 (0) 2022.11.05 Spring에서 자주 쓰이는 디자인 패턴 4 (0) 2022.11.05 Spring에서 자주 쓰이는 디자인 패턴 3 (0) 2022.11.05 다음글이전글이전 글이 없습니다.댓글