Spring Boot controller not working

Some tips to help you debug Spring controller not working. There can be many reasons why this can happen. Here is a checklist:

  1. Have you put appropriate annotations on your class(es)? Putting the SpringApplication annotation will enable Spring Boot autowiring on the package and its sub-packages.
  2. If you are building an uber jar, unzip the jar and verify it meets the Spring Boot executable jar format.
  3. If all else fails, there is no option but to step through the code to see why its not behaving as expected. Run the Spring application in debug mode like this:
LOGGING_LEVEL_ROOT=DEBUG \
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=\*:5005 \
-Dlogging.level.root=debug ...

In separate terminal window type:

jdb -attach 5005

Now you want to set a breakpoint at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan:

stop at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan

Then run the application by typing run:

run

This will start running the application and break when it hits doScan which scans class(es) for bean definitions. A sample call stack looks like this for reference:

[1] org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.findResources (JarUrlClassLoader.java:79)
  [2] java.lang.ClassLoader.getResources (ClassLoader.java:1,483)
  [3] org.springframework.core.io.support.PathMatchingResourcePatternResolver.doFindAllClassPathResources (PathMatchingResourcePatternResolver.java:382)
  [4] org.springframework.core.io.support.PathMatchingResourcePatternResolver.findAllClassPathResources (PathMatchingResourcePatternResolver.java:365)
  [5] org.springframework.core.io.support.PathMatchingResourcePatternResolver.getResources (PathMatchingResourcePatternResolver.java:334)
  [6] org.springframework.core.io.support.PathMatchingResourcePatternResolver.findPathMatchingResources (PathMatchingResourcePatternResolver.java:558) -->
  [7] org.springframework.core.io.support.PathMatchingResourcePatternResolver.getResources (PathMatchingResourcePatternResolver.java:330)
  [8] org.springframework.context.support.AbstractApplicationContext.getResources (AbstractApplicationContext.java:1,442)
  [9] org.springframework.context.support.GenericApplicationContext.getResources (GenericApplicationContext.java:262)
  [10] org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents (ClassPathScanningCandidateComponentProvider.java:422)
  [11] org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents (ClassPathScanningCandidateComponentProvider.java:317)
  [12] org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan (ClassPathBeanDefinitionScanner.java:276)
  [13] org.springframework.context.annotation.ComponentScanAnnotationParser.parse (ComponentScanAnnotationParser.java:128)
  [14] org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass (ConfigurationClassParser.java:289)
  [15] org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass (ConfigurationClassParser.java:243)
  [16] org.springframework.context.annotation.ConfigurationClassParser.parse (ConfigurationClassParser.java:196)
  [17] org.springframework.context.annotation.ConfigurationClassParser.parse (ConfigurationClassParser.java:164)
  [18] org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions (ConfigurationClassPostProcessor.java:415)
  [19] org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry (ConfigurationClassPostProcessor.java:287)
  [20] org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors (PostProcessorRegistrationDelegate.java:344)
  [21] org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors (PostProcessorRegistrationDelegate.java:115)
  [22] org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors (AbstractApplicationContext.java:779)
  [23] org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:597)
  [24] org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh (ServletWebServerApplicationContext.java:146)
  [25] org.springframework.boot.SpringApplication.refresh (SpringApplication.java:738)
  [26] org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:440)
  [27] org.springframework.boot.SpringApplication.run (SpringApplication.java:316)
  [28] org.springframework.boot.SpringApplication.run (SpringApplication.java:1,306)
  [29] org.springframework.boot.SpringApplication.run (SpringApplication.java:1,295)

The best resource I found to learn Spring internals is this talk.

Other Useful Information

Running Spring boot application without Maven

The challenge is how to get the classpath? Today I learned this command from ChatGPT:

tr '\0' ' ' < /proc/<PID>/cmdline

Its an incredibly useful command as it shows the command that launched a Linux process. E.g., when you run a spring boot application using mvn spring-boot:run, above command can reveal the command that Maven runs behind-the-scenes that actually spins up the java process. It looks like this e.g.:

java -classpath /home/ubuntu/apache-maven-3.9.9/boot/plexus-classworlds-2.8.0.jar -Dclassworlds.conf=/home/ubuntu/apache-maven-3.9.9/bin/m2.conf -Dmaven.home=/home/ubuntu/apache-maven-3.9.9 -Dlibrary.jansi.path=/home/ubuntu/apache-maven-3.9.9/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/ubuntu/project-dir org.codehaus.plexus.classworlds.launcher.Launcher spring-boot:run -Dspring-boot.run.jvmArguments=-Djava.library.path=lib -DJWT_SECRET_KEY= -Dspring-boot.run.main-class=my.web.WebApplication -Dspring-boot.run.arguments=--server.port=8080 

It looks like there are 2 java processes that are spawned. One is above and there is yet another java process that is like below:

java -XX:TieredStopAtLevel=1 -Djava.library.path=lib -DJWT_SECRET_KEY= -cp <full_class_path> my.web.WebApplication --server.port=8080

This is incredibly useful as it gives you the full classpath you need if you later want to launch without Maven. Many times I have to use Maven just to launch an application as constructing the humungous classpath is impossible manually.

Pro Tip: Btw you do not want -XX:TieredStopAtLevel=1. In pom.xml always set optimizedLaunch=false (yes intuitively it feels wrong but is the right thing to do) under Spring-Boot-Maven-plugin:

<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<optimizedLaunch>false</optimizedLaunch>                        
                </configuration>
			</plugin>

It will get rid of -XX:TieredStopAtLevel=1 when the spring boot maven plugin launches your application.

This entry was posted in Computers, programming, Software and tagged , , . Bookmark the permalink.

Leave a comment