RequestDispatcher.java
package sprout.mvc.dispatcher;
import sprout.beans.annotation.Component;
import sprout.core.filter.Dispatcher;
import sprout.core.filter.Filter;
import sprout.core.filter.FilterChain;
import sprout.core.interceptor.Interceptor;
import sprout.core.interceptor.InterceptorChain;
import sprout.mvc.advice.ControllerAdviceRegistry;
import sprout.mvc.advice.ExceptionHandlerObject;
import sprout.mvc.advice.ResponseAdvice;
import sprout.mvc.exception.ExceptionResolver;
import sprout.mvc.http.*;
import sprout.mvc.invoke.HandlerMethod;
import sprout.mvc.invoke.HandlerMethodInvoker;
import sprout.mvc.mapping.HandlerMapping;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
@Component
public class RequestDispatcher {
private final HandlerMapping mapping;
private final HandlerMethodInvoker invoker;
private final List<ResponseResolver> responseResolvers;
private final List<ResponseAdvice> responseAdvices;
private final List<Filter> filters;
private final List<Interceptor> interceptors;
private final List<ExceptionResolver> exceptionResolvers;
private final List<DispatchHook> dispatchHooks;
public RequestDispatcher(HandlerMapping mapping,
HandlerMethodInvoker invoker,
List<ResponseResolver> responseResolvers,
List<ResponseAdvice> responseAdvices,
List<Filter> filters,
List<Interceptor> interceptors,
List<ExceptionResolver> exceptionResolvers,
List<DispatchHook> dispatchHooks
) {
this.mapping = mapping;
this.invoker = invoker;
this.responseResolvers = responseResolvers;
this.responseAdvices = responseAdvices;
this.filters = filters;
this.interceptors = interceptors;
this.exceptionResolvers = exceptionResolvers;
this.dispatchHooks = dispatchHooks;
}
public void dispatch(HttpRequest<?> req, HttpResponse res) throws IOException {
try {
for (DispatchHook hook : dispatchHooks) {
hook.beforeDispatch(req, res);
}
new FilterChain(filters, this::doDispatch).doFilter(req, res);
} finally {
for (DispatchHook hook : dispatchHooks) {
hook.afterDispatch(req, res);
}
}
}
private void doDispatch(HttpRequest<?> req, HttpResponse res) {
HandlerMethod hm = null;
Exception caughtException = null;
InterceptorChain interceptorChain = new InterceptorChain(interceptors);
try {
System.out.println(req.getPath() + " " + req.getMethod().toString());
hm = mapping.findHandler(req.getPath(), req.getMethod());
if (hm == null) {
// FIX: BadRequestException 대신 404 응답을 생성
System.err.println("No handler found for: " + req.getMethod() + " " + req.getPath());
res.setResponseEntity(
new ResponseEntity<>("Not Found", null, ResponseCode.NOT_FOUND)
);
return; // 핸들러가 없으므로 즉시 종료
}
if (!interceptorChain.applyPreHandle(req, res, hm)) {
return;
}
Object returnValue = invoker.invoke(hm.requestMappingInfo(), req);
interceptorChain.applyPostHandle(req, res, hm, returnValue);
setResponseResolvers(returnValue, req, res);
} catch (Exception e) { // 컨트롤러 또는 인터셉터에서 예외 발생 시
caughtException = e;
System.err.println("Exception caught in doDispatch: " + e.getMessage());
e.printStackTrace(); // 디버깅용
Object handledReturnValue = null;
for (ExceptionResolver resolver : exceptionResolvers) {
handledReturnValue = resolver.resolveException(req, res, hm, caughtException);
if (handledReturnValue != null) {
// 예외 리졸버가 응답을 직접 설정했을 수도 있고, 반환 값을 주었을 수도 있음
// 반환 값이 있다면 ResponseResolvers로 처리
if (handledReturnValue instanceof ResponseEntity) { // 이미 ResponseEntity라면 직접 설정
res.setResponseEntity((ResponseEntity<?>) handledReturnValue);
} else {
setResponseResolvers(handledReturnValue, req, res);
}
return;
}
}
} finally {
if (hm != null) {
interceptorChain.applyAfterCompletion(req, res, hm, caughtException);
}
}
}
private void setResponseResolvers(Object returnValue, HttpRequest<?> req, HttpResponse res) {
if (res.isCommitted()) return; // 이미 응답 설정된 경우 무시
Object processed = applyResponseAdvices(returnValue, req);
for (ResponseResolver resolver : responseResolvers) {
if (resolver.supports(processed)) {
ResponseEntity<?> responseEntity = resolver.resolve(processed, req);
res.setResponseEntity(responseEntity);
return;
}
}
throw new IllegalStateException("No suitable ResponseResolver found for return value: " + processed);
}
private Object applyResponseAdvices(Object returnValue, HttpRequest<?> req) {
Object current = returnValue;
for (ResponseAdvice advice : responseAdvices) {
if (advice.supports(current, req)) {
current = advice.beforeBodyWrite(current, req);
}
}
return current;
}
}