I. Introduction
The scenario that occurs is described as follows.
First you have a Spring Application with the following directory structure:
And successfully run ready on port 8487:
However, when you make an API call, the HTTP response status is 404?
II. Problem analysis
We want to know why the 404 error occurs, we must first visualize the flow of an HTTP request executing in a Spring Boot application.
When Spring Boot application receives an HTTP request, there is a guy called DispatcherServlet that will dispatch these requests to the respective Controllers. After finding the Controller will have to find a handler method that can handle that request. If a suitable handler method is found, the DispatcherServlet will call the handler method and return the response.
Handler method here is an annotated method with @RequestMapping
annotation or @GetMapping
annotation with HTTP request method (GET/POST..) and specific path.
1 2 3 4 5 6 7 8 9 10 11 12 | #1 @RequestMapping(value = "/404", method = RequestMethod.GET) public Object demo404NotFound(){ return "404 not found"; } #2 @GetMapping("/404") public Object demo404NotFound(){ return "404 not found"; } |
However, in order for a request to be called into the application, Spring Boot needs some pre-processing steps in the application startup process such as initHandlerMethods
. The initHandlerMethods
method is responsible for initializing all handler methods in Spring Boot.
This method will initially look for beans in the ApplicationContext annotated with the @Controller annotation or containing the @RequestMapping annotation. At this point, if we pay attention, we will discover that the cause of the above 404 error is _404Controller
bean not found and this is the culprit.
But we won’t just stop the analysis there, after the initHandlerMethods
method finds a bean that satisfies the requirements like @Controller / @RestController .. then for each bean found, the method goes on to find annotated methods with @ RequestMapping annotation and add it to registry
to use.
1 2 | private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); |
Callstack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | register:654, AbstractHandlerMethodMapping$MappingRegistry (org.springframework.web.servlet.handler) registerHandlerMethod:332, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) registerHandlerMethod:420, RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation) registerHandlerMethod:76, RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation) lambda$detectHandlerMethods$2:299, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) accept:-1, 10885570 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$$Lambda$526) forEach:684, LinkedHashMap (java.util) detectHandlerMethods:297, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) processCandidateBean:266, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) initHandlerMethods:225, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) afterPropertiesSet:213, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) afterPropertiesSet:205, RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation) invokeInitMethods:1863, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) initializeBean:1800, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) doCreateBean:620, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:542, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) lambda$doGetBean$0:335, AbstractBeanFactory (org.springframework.beans.factory.support) getObject:-1, 1896232624 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$227) getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:333, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:208, AbstractBeanFactory (org.springframework.beans.factory.support) preInstantiateSingletons:955, DefaultListableBeanFactory (org.springframework.beans.factory.support) finishBeanFactoryInitialization:918, AbstractApplicationContext (org.springframework.context.support) refresh:583, AbstractApplicationContext (org.springframework.context.support) refresh:147, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context) refresh:734, SpringApplication (org.springframework.boot) refreshContext:408, SpringApplication (org.springframework.boot) run:308, SpringApplication (org.springframework.boot) run:1306, SpringApplication (org.springframework.boot) run:1295, SpringApplication (org.springframework.boot) main:14, SpringBean404Application (com.logbasex) |
After completing the startup application and initHandlerMethods, we will proceed to plug in the debugging point in the controller and call the API to see how Spring Boot receives and processes the HTTP request.
Callstack:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | createWithResolvedBean:372, HandlerMethod (org.springframework.web.method) getHandlerInternal:384, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler) getHandlerInternal:125, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method) getHandlerInternal:67, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method) getHandler:498, AbstractHandlerMapping (org.springframework.web.servlet.handler) getHandler:1264, DispatcherServlet (org.springframework.web.servlet) doDispatch:1046, DispatcherServlet (org.springframework.web.servlet) doService:963, DispatcherServlet (org.springframework.web.servlet) processRequest:1006, FrameworkServlet (org.springframework.web.servlet) doGet:898, FrameworkServlet (org.springframework.web.servlet) service:655, HttpServlet (javax.servlet.http) service:883, FrameworkServlet (org.springframework.web.servlet) service:764, HttpServlet (javax.servlet.http) internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilter:53, WsFilter (org.apache.tomcat.websocket.server) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:100, RequestContextFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:93, FormContentFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) invoke:197, StandardWrapperValve (org.apache.catalina.core) invoke:97, StandardContextValve (org.apache.catalina.core) invoke:541, AuthenticatorBase (org.apache.catalina.authenticator) invoke:135, StandardHostValve (org.apache.catalina.core) invoke:92, ErrorReportValve (org.apache.catalina.valves) invoke:78, StandardEngineValve (org.apache.catalina.core) service:360, CoyoteAdapter (org.apache.catalina.connector) service:399, Http11Processor (org.apache.coyote.http11) process:65, AbstractProcessorLight (org.apache.coyote) process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:49, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads) run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads) run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:750, Thread (java.lang) |
References
===
Thanks for reading.