웹 환경에서만 동작하는 스코프
- request : HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.
- session : HTTP Session과 동일한 생명주기를 가지는 스코프
- application : 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프
- websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프
build.gradle에 추가하고, CoreApplication
의 main 메서드가 잘 실행되는지 확인한다.
- 기대하는 공통 포맷 : [UUID][requestURL]{message}
- UUID를 사용해서 HTTP 요청 구분
- requestURL 정보도 추가로 넣어서 어떤 URL을 요청해서 남은 로그인지 확인
@Component
@Scope(value="request")
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message) {
System.out.println("[" + uuid + "]" + "{" + requestURL + "}" + message);
}
@PostConstruct
public void init(){
uuid = UUID.randomUUID().toString();
System.out.println("[ "+uuid+" ] request scope bean create:" + this);
}
@PreDestroy
public void close(){
System.out.println("[ "+uuid+" ] request scope bean close:" + this);
}
}
MyLogger
클래스@Scope(value="request")
를 사용해서 request 스코프로 지정@PostConstructor
초기화 메서드를 사용해 uuid 생성해서 저장@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";
}
}
http://localhost:8080/log-demo
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
[d06b992f...] request scope bean create
[d06b992f...][http://localhost:8080/log-demo] controller test
[d06b992f...][http://localhost:8080/log-demo] service id = testId
[d06b992f...] request scope bean close
But... 오류발생!
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final ObjectProvider<MyLogger> myLoggerProvider;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURL().toString();
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + id);
}
}
ObjectProvider
덕분에 ObjectProvider.getObject()
를 호출하는 시점까지 request.scope 빈의 생성을 지연할 수 있다.ObjectProvider.getObject()
를 호출하는 시점에 HTTP 요청이 진행중이므로 request scope 빈의 생성이 정상 처리된다.ObjectProvider.getObject()
를 LogDemoController , LogDemoService
에서 각각 한번씩 따로 호출해도 같은 HTTP 요청이면 같은 스프링 빈이 반환된다.@Component
@Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
}
TARGET_CLASS
를 선택INTERFACES
선택CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.
@Scope
의 proxyMode = ScopedProxyMode.TARGET_CLASS)
를 설정하면 스프링 컨테이너는 CGLIB라는 바이트코드를 조작하는 라이브러리를 사용해서, MyLogger를 상속받은 가짜 프록시 객체를 생성한다.가짜 프록시 객체는 요청이 오면 그때 내부에서 진자 빈을 요청하는 위임 로직이 들어있다.