반응형

# 스프링 핵심 원리 기본편

# 스코프와 프록시

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
}
  • proxyMode = ScopedProxyMode.TARGET_CLASS를 추가.
  • 적용 대상이 인터페이스가 아닌 클래스라면 TARGET_CLASS를 선택해준다.
  • 적용 대상이 인터페이스라면 INTERFACES를 선택해준다.
  • 위 방법을 통해 MyLogger의 가짜 프록시를 생성, HTTP request와 상관 없이 가짜 프록시 클래스를 다른 반에 미리 주입해 둘 수 있다.
  • LogoDemoController
package hello.core.web;
import hello.core.common.MyLogger;
import hello.core.logdemo.LogDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequiredArgsConstructor
public class LogDemoController {
   private final LogDemoService logDemoService;
	private final MyLogger myLogger;
 
	@RequestMapping("log-demo")
	@ResponseBody
	public String logDemo(HttpServletRequest request) {
		String requestURL = request.getRequestURL().toString();
		myLogger.setRequestURL(requestURL);
		myLogger.log("controller test");
		logDemoService.logic("testId");
		
        return "OK";
	}
}
  • LogoDemoService
package hello.core.logdemo;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class LogDemoService {
	private final MyLogger myLogger;
	
    public void logic(String id) {
		myLogger.log("service id = " + id);
	}
}

## 웹 스코프와 프록시 동작 원리

  • myLogger 출력
System.out.println("myLogger = " + myLogger.getClass());


// 출력 결과
myLogger = class hello.core.common.MyLogger$$EnhancerBySpringCGLIB$$b68b726d

### CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 객체를 만들어 주입한다.

  • @Scope의 ProxyMode = ScopedProxyMode.TARGET_CLASS를 설정하면 스프링 컨테이너는 CGLIB라는 바이트코드를 조작하는 라이브러리를 사용, MyLogger를 상속받은 가짜 프록시 객체를 생성한다.
  • 결과 확인 시 MyLogger$ $EnhancerBySpringCGLIB 라는 클래스로 만들어진 객체가 대신 등록된 것을 알 수 있다.
  • 스프링 컨테이너에는 myLogger라는 이름으로 진짜 대신 가짜 프록시 객체를 등록한다.
  • ac.getBean("myLogger", MyLogger.class)를 이용하여 조회 시에도 프록시 객체가 조회되는 걸 확인할 수 있다.
  • 의존관계 주입 시에도 가짜 프록시가 주입된다.

### 가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 드렁있다.

  • 가짜 프록시 객체는 내부의 진짜 myLogger를 찾는 방법을 알고 있음.
  • 클라이언트가 myLogger.logic()를 호출하면 가짜 프록시 객체의 메서드를 호출한 것
  • 가짜 프록시 객체는 원본 클래스를 상속 받아서 만들어졌기 때문에 해당 객체를 사용하는 클라이언트 입장에 서는 원본인지 아닌지 모르게, 동일하게 사용할 수 있다 (다형성)

### 동작 정리

  • CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.
  • 가짜 프록시 객체는 실제 요청이 오면 그때 내부에서 실제 빈을 요청하는 위임 로직이 들어있다. 가짜 프록시 객체는 실제 request scope와는 관계가 없다. 그냥 가짜이고, 내부에 단순한 위임 로직만 있고, 싱글톤 처럼 동작한다.

## 특징 정리

  • 프록시 객체 덕분에 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 request scope를 사용할 수 있다.
  • Provider를 사용하든, 프록시를 사용하든 핵심은 진짜 객체 조회를 꼭 필요한 시점까지 지연 처리 한다는 점. 단지 애노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체할 수 있다. 이것이 바로 다형성과 DI 컨 테이너가 가진 큰 강점.
  • 꼭 웹 스코프가 아니어도 프록시는 사용할 수 있다.

## 주의점

  • 싱글톤을 사용하는 것 같지만 다르게 동작하기 때문에 결국 주의해서 사용해야 한다. 이런 특별한 scope는 꼭 필요한 곳에만 최소화해서 사용하자, 무분별하게 사용하면 유지보수하기 어려워 진다.

 

출처 : 인프런 스프링 핵심 원리 기본편

반응형

+ Recent posts