해결된 질문
작성
·
420
답변 2
3
안녕하세요. gto1997, 공식 서포터즈 David입니다.
@Controller
@RequiredArgsConstructor
public class ProxyController {
private final ProxyFoo proxyFoo;
@GetMapping("/proxy")
public ResponseEntity<Void> proxy() {
proxyFoo.print();
return ResponseEntity.ok().build();
}
}
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ProxyFoo {
public ProxyFoo() {
System.out.println("ProxyFoo.ProxyFoo");
}
@PostConstruct
void init() {
System.out.println("ProxyFoo.init");
}
public void print() {
System.out.println("ProxyFoo.print");
}
}
빈의 스코프가 request인데 proxyMode가 적용되지 않았을 때를 생각해면, ProxyController에서 ProxyFoo를 의존하고 있고 생성자 주입을 사용하고 있기 때문에 ProxyController를 생성할 때 ProxyFoo를 주입 받으려 할 것입니다. ProxyFoo를 ProxyController에 주입하기 위해서 ProxyFoo를 생성해야 합니다.
이때, 오류가 발생합니다. 왜냐하면 ProxyFoo의 스코프가 request이기 때문입니다. request 스코프는 요청이 들어왔을 때만 빈을 생성할 수 있는데, 지금은 요청이 들어온 게 아니라 애플리케이션 실행 중 ProxyController를 생성하는 과정이기 때문에 오류가 발생하는 것입니다.
이런 오류를 방지하기 위해 proxyMode=TARGET_CLASS를 추가하여 ProxyFoo를 생성하는 과정에 ProxyFoo를 생성하는 게 아니라 ProxyFoo를 대체할 ProxyFoo의 프록시를 생성합니다. 그리고 이렇게 생성된 ProxyFoo의 프록시 객체를 ProxyController에 주입합니다. 그러면 request 스코프인 ProxyFoo를 생성하지 않기 때문에 오류없이 ProxyController를 생성할 수 있게 됩니다. 즉, ProxyFoo의 생성을 지연(실제 요청이 들어와서 ProxyFoo를 호출할 때까지)하고 ProxyFoo 대체제(ProxyFoo의 프록시)를 주입하는 것입니다.
@PostConstruct는 객체가 생성된 후 메서드가 실행되도록 하는 애노테이션입니다. 따라서, ProxyFoo의 프록시가 생성되고 나서 실행되지 않고 ProxyFoo가 생성되고 난 후 실행될 것입니다.
요청이 들어와 ProxyFoo를 호출할 때(proxyFoo.print()) ProxyFoo의 프록시 객체가 실제 ProxyFoo에게 print() 호출을 위임하게 됩니다. 이때, ProxyFoo가 생성되어 있지 않은 상황이기 때문에 내부적으로 프록시가 아닌 실제 ProxyFoo 객체를 생성하게 됩니다. 이렇게 실제 ProxyFoo 객체를 생성하는 과정에서 ProxyFoo 객체가 생성되고 난 후 @PostConstruct가 붙은 init()이 호출되게 됩니다.
따라서, 위 코드를 기준으로 애플리케이션을 실행했을 때 콘솔에 출력되는 건 아무것도 없으며 /proxy
로 접근하게 되면 그제서야 아래와 같이 출력됩니다.
ProxyFoo.ProxyFoo
ProxyFoo.init
ProxyFoo.print
감사합니다.
아 저는 가짜 프록시 객체가 생성될 때 @PostConsturct 실행하고 실제 요청이 들어온 후 실제 MyLogger 빈이 등록될 때 @PostConstruct 실행해서 총 2번 발생한다고 생각이들어서 질문을 드렸습니다.