스프링은
"어떤 대상을 주입할지"
"어떤 방식으로 주입할지"
를 철저히 "스프링의 관심사" 에 따라 결정합니다.
@RequestMapping 같은 어노테이션은,HttpServletRequest 같은 경우,즉, Bean이 아니어도, 구현체나 스프링 관심사에 맞으면 주입할 수 있다.
(여기서는 웹 의존성이 관심사인데, 이미 이것을 잘 구현해놓은 WAS(톰캣등)을 재활용할 수 있습니다.)
예를 들어 아래처럼 컨트롤러 메서드에 HttpServletRequest를 파라미터로 받을 때...
@RequestMapping("/signup")
public void signup(HttpServletRequest request) {
// ...
}
"어디에도 @Autowired가 없는데 어떻게 주입되지?"
라는 의문이 생깁니다.
Controller 빈 생성
↓
(파라미터 분석 준비)
↓
DispatcherServlet 동작
↓
HandlerAdapter 동작
↓
HandlerMethodArgumentResolver 적용
↓
HttpServletRequest 타입 파라미터 발견
↓
RequestContextHolder를 통해 현재 요청 객체 조회 - 각 request 요청이 비동기로 동작하기때문에 이렇게 함
↓
HttpServletRequest 인스턴스 반환 및 메서드 호출 시 주입
@Autowired 등으로 관리하는 것.RequestContextHolder를 통해 현재 요청(request) 을 관리합니다.즉,
"지금 이 순간 진행 중인 요청" 에서 꺼내서 주입해주는 거지,
"컨테이너가 관리하는 싱글톤 빈" 은 아닙니다!
핵심은 RequestMappingHandlerAdapter 와 HandlerMethodArgumentResolver 입니다.
public class RequestMappingHandlerAdapter implements HandlerAdapter {
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 핸들러 메서드 호출
return invokeHandlerMethod(request, response, (HandlerMethod) handler);
}
}
→ 이 invokeHandlerMethod 안에서
→ 메서드 파라미터 하나하나를
→ HandlerMethodArgumentResolver 들이 처리합니다.
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return ServletRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
Principal.class.isAssignableFrom(paramType);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
// HttpServletRequest 반환
return webRequest.getNativeRequest(parameter.getParameterType());
}
}
HttpServletRequest 타입 파라미터 요청이 옴여기서 볼 수 있듯이,
HttpServletRequest는 Bean이 아닙니다.
하지만 스프링 MVC에서는
현재 웹 요청에 필요한 기본 객체 를
따로 관리하고 꺼내 쓸 수 있도록 만들어져 있습니다.
@Override
@Nullable
public <T> T getNativeRequest(@Nullable Class<T> requiredType) {
if (requiredType != null) {
if (requiredType.isInstance(this.request)) {
return (T) this.request;
}
}
return null;
}
스프링은
필요한 관심사에 따라 필요한 것만 직접 관리하려 합니다.
이번 주제에서는)
빈 관리가 필요한 것들은 컨테이너가 관리한다.
웹 요청에만 필요한 것들은 WAS, RequestContextHolder 등으로 관리한다. - 이건 WAS 기본 스펙
그래서 모든것이 빈이 아니어도 기존 WAS등을 재활용함.
싱글톤 빈 = 전반적으로 애플리케이션에서 사용할려는 것
메서드 파라미터 유추가 아닌 Autowired 경우 스프링이 관리한다고 보는게 맞다.
근데 여전히 빈은 아님.
AutowiredAnnotationBeanPostProcessor가 HttpServletRequest 타입 필드를 찾음
DefaultListableBeanFactory.resolveDependency() 호출
resolvableDependencies를 확인
거기서 RequestObjectFactory 꺼냄
RequestObjectFactory.getObject() 호출
RequestContextHolder.currentRequestAttributes() 호출
현재 쓰레드의 ThreadLocal에 저장된 RequestAttributes 가져옴
거기서 HttpServletRequest를 꺼내서 주입
@Autowired로 HttpServletRequest를 요청하면,
스프링은 "빈 등록 X → 대신 특별 관리 대상 → RequestContextHolder에서 꺼내오기" 전략을 씀.



private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 메서드 파라미터 정보 가져오기
MethodParameter[] parameters = getMethodParameters();
// 각 파라미터를 저장할 배열
Object[] args = new Object[parameters.length];
// 각 파라미터 처리
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 이미 제공된 인자가 있는지 확인
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 중요! HandlerMethodArgumentResolver 사용
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException("No suitable resolver for parameter " + parameter);
}
try {
// 여기서 실제로 인자 해결 (이 부분이 핵심!)
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// 예외 처리...
}
}
return args;
}
타입에따라 다 각각 어노테이션으로 다 지원함!
// 스프링 MVC가 ArgumentResolver를 설정하는 코드 일부
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
// 애노테이션 기반 리졸버들
resolvers.add(new RequestParamMethodArgumentResolver(...));
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new RequestHeaderMethodArgumentResolver(...));
resolvers.add(new CookieValueMethodArgumentResolver(...));
// 특수 타입들
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(...));
// 모델, 세션, 요청 속성
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new SessionAttributesMethodArgumentResolver());
// 그 외 다수...
return resolvers;
}
궁금한것들: