Profile Photo

Internal working of spring web application

Created on: Sep 30, 2024

This article explain internal working behind spring boot web which is widely used to expose rest api. Let's take example from a sample spring boot application. We can start by by adding below dependency in maven based project and add add a controller with basic code.

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
package com.example.adapter.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/hello") public class HelloController { @GetMapping public String greetings(){ return "Hello world"; } }

When a client sends a request, it is intercepted by DispatcherServlet. DispatcherServlet first determines which handler (controller) will process the request. Then DispatcherServlet uses RequestMappingHandlerAdapter to invoke corresponding method. Since HelloController is annotated with @RequestMapping it used RequestMappingHandlerAdapter. RequestMappingHandlerAdapter invokes greetings() method on HelloController which returns Hello world. Then RequestMappingHandlerAdapter invokes the function return ModelAndView which return model and view in MVC pattern. This is used to view and specify which view to render.

// This is like pseudo code for the action that happens inside Dispatcher servlet. public void doDispatch(HttpServletRequest request, HttpServletResponse response) { try { HandlerExecutionChain handlerChain = getHandler(request); if (handlerChain == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } HandlerAdapter handlerAdapter = getHandlerAdapter(handlerChain.getHandler()); ModelAndView mv = handlerAdapter.handle(request, response, handlerChain.getHandler()); processDispatchResult(request, response, handlerChain, mv, handlerAdapter); } catch (Exception e) { processHandlerException(request, response, e); } } public void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain handlerChain, ModelAndView mv, HandlerAdapter handlerAdapter) { if (mv != null && !mv.wasCleared()) { render(mv, request, response); } else { ResponseEntity<String> responseEntity = (ResponseEntity<String>) mv.getModel().get("responseEntity"); HttpMessageConverter<String> converter = new StringHttpMessageConverter(); converter.write(responseEntity.getBody(), MediaType.TEXT_PLAIN, new ServletServerHttpResponse(response)); response.setStatus(responseEntity.getStatusCode().value()); } }

In processDispatchResult method, check if we can process the result as a view or response body. Since we have used @RestController, It will use HttpMessageConverter to serialize the return value and write it into the HTTP response.