Spring WebFlux 는 스프링 5.0 부터 지원하는 리액티브 웹 프레임워크 입니다. Spring MVC 에서 처리하지 못하는 많은 요청량을 처리하기 위해 만들어진 비동기 논블로킹 방식의 프레임워크 입니다.
ServerWebExchange 는 WebFilter 체인에서 전처리 과정을 거친 후 WebHandler 인터페이스의 구현체인 DispatcherServlet 에게 전달됨
DispatcherHandler 에서 HandlerMapping List 를 원본 Flux 의 소스로 전달받음
ServerWebExchange 를 처리할 핸들러 조회
조회한 핸들러의 호출을 HandlerAdapter 에게 위임
HandlerAdapter 은 ServerWebExchange 를 차리할 핸들러를 호출
Controller / HandlerFunction 형태의 핸들러에서 요청을 처리한 후, 응답 데이터 리턴
핸들러로부터 리턴받은 응답 데이터를 처리할 HandlerResultHandler 조회
조회한 HandlerResultHandler 가 응답 데이터를 적절히 처리 후 response 로 응답
[코드 15.1]
public interface HttpHandler {
Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response);
}
[코드 15.2]
public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler {
...
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
...
ServerWebExchange exchange = createExchange(request, response);
}
}
WebFilter 은 Spring MVC 서블릿 필터처럼 핸들러가 요청을 처리하기 전에 전처리 작업을 할 수 있도록 함
[코드 15.3]
public interface WebFilter {
Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);
}
[코드 15.4]
@Component
public class BookLogFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
return chain.filter(exchange).doAfterTerminate(() -> {
if (path.contains("books")) {
System.out.println("path: " + path + ", status: " +
exchange.getResponse().getStatusCode());
}
});
}
}
함수형 기반의 요청 핸들러에 적용할 수 있는 Filter
[코드 15.5]
public interface HandlerFilterFunction<T, R> {
Mono<R> filter(ServerRequest request, HandlerFunction<T, R> next);
}
[코드 15.6]
public class BookRouterFunctionFilter implements HandlerFilterFunction {
@Override
public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction next) {
String path = request.requestPath().value();
return next.handle(request).doAfterTerminate(() -> {
System.out.println("path: " + path + ", status: " +
request.exchange().getResponse().getStatusCode());
});
}
}
[코드 15.7]
@Configuration
public class BookRouterFunction {
@Bean
public RouterFunction routerFunction() {
return RouterFunctions
.route(GET("/v1/router/books/{book-id}"),
(ServerRequest request) -> this.getBook(request))
.filter(new BookRouterFunctionFilter());
}
public Mono<ServerResponse> getBook(ServerRequest request) {
return ServerResponse
.ok()
.body(Mono.just(BookDto.Response.builder()
.bookId(Long.parseLong(request.pathVariable("book-id")))
.bookName("Advanced Reactor")
.author("Tom")
.isbn("222-22-2222-222-2").build()), BookDto.Response.class);
}
}
[코드 15.8]
public class DispatcherHandler implements HttpHandler {
private final HandlerMapping handlerMapping;
private final HandlerAdapter handlerAdapter;
public DispatcherHandler(HandlerMapping handlerMapping, HandlerAdapter handlerAdapter) {
this.handlerMapping = handlerMapping;
this.handlerAdapter = handlerAdapter;
}
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
ServerWebExchange exchange = createServerWebExchange(request, response);
return invokeHandler(exchange);
}
private ServerWebExchange createServerWebExchange(ServerHttpRequest request, ServerHttpResponse response) {
// Create a ServerWebExchange object based on the request and response
return new DefaultServerWebExchange(request, response, handlerAdapter.getMessageReaders());
}
private Mono<Void> invokeHandler(ServerWebExchange exchange) {
// Find the appropriate handler for the given request
Mono<HandlerExecutionChain> chainMono = handlerMapping.getHandler(exchange);
// Handle the request using the handler
return chainMono.flatMap(chain -> {
try {
return handlerAdapter.handle(exchange, chain.getHandler());
} catch (Exception e) {
return Mono.error(e);
}
}).flatMap(result -> handleResult(result, exchange));
}
private Mono<Void> handleResult(HandlerResult result, ServerWebExchange exchange) {
// Handle the result of the handler execution
return handlerAdapter.handleResult(exchange, result);
}
}
initStrategies(ApplicationContext context)
BeanFactoryUtils 를 이용하여 ApplicationContext 로 부터 아래 Bean 들 검색
HandlerMapping Bean => List<HandlerMapping>
HandlerAdapter Bean => List<HandlerAdapter>
HandlerRequestHandler Bean => List<HandlerRequestHandler>
handle(ServerWebExchange exchange)
List<HandlerMapping>
을 원본 데이터 소스로 입력받은 후 getHandler
메서드를 통해 매치되는 핸들러 중 첫 핸들러 사용
invokeHandler
을 통해 핸들러 호출 위임
invokeHandler()
내부에서 Handle 객체와 매핑되는 HandlerAdapter
을 통해서 이루어짐handleResult
를 통해 응답 처리를 위임handlerResult()
내부에서 호출한 getResultHandler()
에서 HandlerResult
객체와 매핑되는 HandlerResultHandler
을 통해서 이루어짐블로킹 방식은 스레드 풀로 여러개의 스레드를 관리하며 한 스레드가 한 요청을 처리함 (한 스레드가 차단)
논블로킹은 스레드가 차단되지 않아 적은 수의 스레드 풀로도 더 많은 요청 처리 가능 (이벤트 루프 방식)
모든 작업들이 이벤트로 처리되기에 이벤트 발생 시 해당 이벤트에 대한 콜백 등록하며 다음 이벤트 처리로 넘어감