요청시작
DistpatcherServlet 에서 doDispatch 호출
적절한 HandlerAdapter 얻어야 함
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
get 메소드로 호출
RequestMappingHandlerAdapter 받음
실제 handler 호출
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mav = this.invokeHandlerMethod(request, response, handlerMethod);
invokeHandlerMethod 로직이 많지만 modelAttribute 관련로직 살펴볼 것
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
handlerMethod 와 binderFactory 를 넣어서 modelFactory 를 구한다
getModelFactory 자세히 보면
Set<Method> methods = (Set)this.modelAttributeCache.get(handlerType);
handlerType은 ModelAttributeController 다
조건을 통해 method를 뽑아낸다
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
메소드 얻어서 저장
e, MODEL_ATTRIBUTE_METHODS);
for 문을 돌면서 modelAttribute 메소드에 넣는다
for(Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(this.createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
if (this.argumentResolvers != null) {
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
attrMethod.setDataBinderFactory(factory);
return attrMethod;
}
HandlerMethod 엔 bean 과 method를 가지고 있어 bean 에 해당하는 메소드를 호출한다
HandlerMethod 를 상속한 클래스가 InvocableHandlerMethod 다 이건 reflaction 기술을 사용해서 handler 메소드를 호출한다
이 클래스는 servlet(웹) 전용은 아니다 InvocableHandlerMethod 이걸 상속해서 만든 ServletInvocableHandlerMethod 클래스는 웹전용
bean 타입과 메소드 정보만 있으면 InvocalbleHandlerMethod로 타겟 메소드를 호출할 수 있다
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
그리고 메소드에 파라미터가 있다면 argumentResolver 찾아서 set 한다
if (this.argumentResolvers != null) {
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
결국 bean 정보, 메소드, 파라미터 세 속성을 가지고 attrMethod를 뽑아낸다
이렇게 해서 ModelFactory를 만든다
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
handler 메소드 호출
ServletInvocableHandlerMethod 에는 returnValuesHandler도 넣어야 한다
웹전용이기에 응답부분도 처리 그래서 4개 처리
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
모델을 담을 수 있는 컨테이너 생성
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
initModel 하면 모델을 호출한다
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
this.invokeModelAttributeMethods(request, container);
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
while(!this.modelMethods.isEmpty()) {
InvocableHandlerMethod modelMethod = this.getNextModelMethod(container).getHandlerMethod();
ModelAttribute ann = (ModelAttribute)modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
} else {
Object returnValue = modelMethod.invokeForRequest(request, container, new Object[0]);
if (modelMethod.isVoid()) {
if (StringUtils.hasText(ann.value()) && logger.isDebugEnabled()) {
logger.debug("Name in @ModelAttribute is ignored because method returns void: " + modelMethod.getShortLogMessage());
}
} else {
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
}
modelMethods 에 정의한 메소드가 들어있다
modelAttribute 가 선언돼있는지 확인
InvocableHandlerMethod modelMethod = this.getNextModelMethod(container).getHandlerMethod();
ModelAttribute ann = (ModelAttribute)modelMethod.getMethodAnnotation(ModelAttribute.class);
Object returnValue = modelMethod.invokeForRequest(request, container, new Object[0]);
파라미터가 있으면 파라미터 만들어서 넣어줌
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
Object returnValue = this.doInvoke(args);
doInvoke 해서 handler의 실제 메소드 호출한다
이제 리턴한 값을 모델에 담는다
return returnValue;
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
returnValueName 은 객체 소문자로 만든다
retunrValueName 으로 저장된 것이 없으면 저장한다
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
이제 정말로 호출하기 위한 메소드 실행
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
똑같이 invokeForRequest 실행
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
this.setResponseStatus(webRequest);
if (returnValue == null) {
if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
this.disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
if (logger.isTraceEnabled()) {
logger.trace(this.formatErrorForReturnValue(returnValue), var6);
}
throw var6;
}
}