Source Code Investigation
Call Stack
-
org.springframework.boot.SpringApplication.run-
org.springframework.boot.SpringApplication.prepareEnvironment-
org.springframework.boot.SpringApplication.configureEnvironmentorg.springframework.boot.SpringApplication.configurePropertySources
-
-
PropertySource
-
Testing
-
Precedence (high to low)
-
DynamicValuesPropertySource@DynamicPropertySource -
@TestPropertySource-propertiesattribute -
@TestPropertySource-locationsattribute
-
-
-
PropertySourceLoaderPropertiesPropertySourceLoaderfor loading.propertiesfilesYamlPropertySourceLoaderfor loading.yamland.ymlfiles
Default precedence order, from highest to lowest, in PropertySource name
-
configurationPropertiesorg.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySourceTo be used with
PropertyResolveror added to theEnvironment -
servletConfigInitParamsIn the case that the application is not a Java Servlet application, this
PropertySourcewill be an instance ofStubPropertySource. -
servletContextInitParamsIn the case that the application is not a Java Servlet application, this
PropertySourcewill be an instance ofStubPropertySource. -
systemPropertiesorg.springframework.core.env.PropertiesPropertySource -
systemEnvironmentSystemEnvironmentPropertySourceEnvironmentPostProcessor.OriginAwareSystemEnvironmentPropertySource -
randomRandomValuePropertySource
Recipes
Config
-
Resources
-
Notes
- Avoid using annotation scanning, which tends to spread bean definitions all over the place. Instead, centralize them in the
@SpringBootApplicationclass file. - If there are really too many bean definitions to put into one single file, create a subdirectory under the root package named
config, and dedicate it to@Configurationclasses.
- Avoid using annotation scanning, which tends to spread bean definitions all over the place. Instead, centralize them in the
Initialize a new project
-
Spring Initializr - Website (opens in a new tab)
Spring Initializris also aRESTweb service, so ifcurl https://start.spring.io, a quickstart guide will display, or directly supply parameters to create a project:curl -G https://start.spring.io/starter.zip \ -d dependencies=web \ -d javaVersion=11 -o demo.zip -
Spring Boot CLI (opens in a new tab)
spring init \ -d=data-rest,devtools,actuator,lombok,cloud-feign,prometheus,testcontainers \ -j=<JAVA_VERSION> \ --build=<maven/gradle> \ -x \ -g=<GROUP_ID> \ -a=<ARTIFACT_ID> \ -n=<PROJECT_NAME> \ --package-name=<PACKAGE_NAME> \ --description=<DESCRIPTION> \ <DIRECTORY_NAME>
Override Spring config properties at runtime
-
Environment variable
If the property is
spring.xxx.yyy, the corresponding environment variable isSPRING_XXX_YYYe.g.
SPRING_PROFILES_ACTIVE=<profile1[,profile2...]> -
JVM system property
Use
-DJVM option, property name is the same as the target propertye.g.
-Dspring.profiles.active=<profile1[,profile2...]> -
CLI argument
Use
--<property=value>e.g.
--server.port=9000 --logging.level.org.springframework.boot=trace
Spring config locations
-
spring.config.nameandspring.config.locationare used very early to determine which files have to be loaded. They must be defined as an environment property (typically an OS environment variable, a system property, or a command-line argument).java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties -
spring.config.namedefaults toapplication.properties, whilespring.config.locationdefaults to the following locations:file:./config/file:./classpath:/config/classpath:/
-
When custom config locations are configured by using
spring.config.additional-location, they are used in addition to the default locations. Additional locations are searched before the default locations. -
Resources
Verify if a property source file is loaded successfully
Change logging level to trace for org.springframework.boot.context.config package
logging:
level:
org.springframework.boot.context.config: traceLook for Adding imported property source in the log output
Access Environment Variables
-
- Java SE:
System.getenv("ENVIRONMENT_VARIABLE") -
- Spel:
@Value("#{systemEnvironment['ENVIRONMENT_VARIABLE']}") -
- Spring Environment class:
private final Environment environment; public App(Environment environment) { this.environment = environment; } var envPath = environment.getProperty("ENVIRONMENT_VARIABLE");
Track which property is imported from which property source
-
Use
/actuator/envendpoint, it will list allPropertySourceand their properties. -
org.springframework.boot.context.config.ConfigDataImporter.load// loaded variable contains PropertySource and the retrieved properties ConfigData loaded = this.loaders.load(loaderContext, resource); if (loaded != null) { this.loaded.add(resource); this.loadedLocations.add(location); result.put(candidate, loaded); }
List all active Auto Configuration instances with interface types
- #TODO
Bean lazy initialization
Global flag:
spring:
main:
lazy-initialization: trueCaveats:
- Might cause
AutoConfigurationnot to be loaded
Exclude specific Auto Configuration classes
- In Java code
-
Use
@SpringBootApplication(exclude = <AutoConfigurationClass.class>) -
Use config property
spring.autoconfigure.exclude=<AutoConfigurationClass.class>
Activate profiles in the order of precedence
@ActiveProfiles({"db", "kafka", "aws", "ccom"})The order of activation is from left to right, so the order of precedence is ccom, aws, kafka, db.
-
Rules of thumb
@ActiveProfilesshould be used withapplication entry point class, such asApplication class (main method holder)or test cases.@Profileshould be used withgeneric Spring components
Loading Spring config properties from classpath
-
Classes under
src/main/javadirectory and classes undersrc/test/javause different classloaders. -
Classes under
src/main/javawill only load classpath Resources fromsrc/main/resourcesdirectory. -
Test classes under
src/test/javawill load classpath Resources both fromsrc/main/resourcesandsrc/test/resourcesdirectory. -
For test classes, test classpath Resources take precedence over classpath Resources if they have the same name.
For example, suppose we have the following application config files with active Profiles:
db,aws.src/main/resources/application.properties src/main/resources/application-db.properties src/test/resources/application.properties src/test/resources/application-aws.propertiesThe following application config files will be loaded in the following order:
test-classes/application.properties test-classes/application-aws.properties classes/application-db.properties
Spring config management strategy
-
application.properties- Should contain the default configuration for the application including test cases.
- There's only one
application.propertiesfile undersrc/main/resourcesby default and noapplication.propertiesfile undersrc/test/resources. - Since only one Spring config with the same name is allowed, the
application.propertiesfile undersrc/test/resourceswill override theapplication.propertiesfile undersrc/main/resources. Therefore you can't useapplication-aws.propertiesinsrc/test/resourcesto overrideapplication-aws.propertiesinsrc/main/resources. Instead you will lose config properties fromapplication-aws.propertiesinsrc/main/resourceswhen running test cases.
-
Solutions to have overriding effect for running test cases
-
Every profile config should have unique config properties without any overlapping
-
Prepare profiles and config as follows:
src/main/resources/application-aws.propertiessrc/test/resources/application-aws-test.properties
And activate profiles like:
@ActiveProfiles({"aws", "aws-test"})Then the properties from
src/test/resources/application-aws-test.propertieswill override the properties fromsrc/main/resources/application-aws.propertiesif they have the same name.Cons: more profiles to manage
-
Alternatively, put all overriding overriding properties into a single config file named
application-test.propertiesundersrc/test/resources.Every test case must activate the profile
testlike:@ActiveProfiles("test")Cons: might override unintended properties, in that case, use inline config to override again.
-
Persistence
Database Initialization
-
Spring Boot - AutoConfiguration
org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties@ConfigurationProperties("spring.sql.init") -
Schema creation
-
In a
JPA-based app, you can choose to letHibernatecreate the schema or useSpring Bootto run DDL inschema-[spring.datasource.platform].sql, but you cannot do both. Make sure to disablespring.jpa.hibernate.ddl-autoif you useschema-[spring.datasource.platform].sql. -
Using Spring Boot to create schema
-
Turn off
HibernateautoDDLgeneration function# hibernate.hbm2ddl.auto spring.jpa.hibernate.ddl-auto=none -
Enable initialization mode as always
spring.datasource.initialization-mode=always spring.sql.init.mode=always (Spring Boot 2.5.0+) -
Specify RDBMS provider
# This is for Spring Boot, and must be specified for schema-${spring.datasource.platform}.sql and data-${spring.datasource.platform}.sql to be executed. spring.datasource.platform=mysql spring.sql.init.platform=mysql (Spring Boot 2.5.0+) # This is for JPA # spring.jpa.database=mysql -
Ensure database is created beforehand
# DB_NAME cannot be specified if it does not exist before the web application starts as database initialiaztion script (schema-${spring.datasource.platform}.sql) is run after the connection is obtained. spring.datasource.url=jdbc:mysql://${HOST}:${PORT}/${DB_NAME}
-
-
-
Data population
-
Using Spring Boot to populate data
-
spring.datasource.initialization-modedefaults toembeddedinorg.springframework.boot.autoconfigure.jdbc.DataSourceProperties -
org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.isEnableddetermines if data script is run or not. -
Debug
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerfor possible issues. The statements in the a script file are run one by one, not altogether, therefore statement order matters and foreign key constraints will get in the way. -
org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScriptruns the initialization SQL script (data.sql), which separates statements by;(semi-colon) by default, if missing\nis used instead. This could break SQL statements in an undesirable manner and cause SQL exceptions.
-
-
Turn on Hibernate Statistics
spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.engine.internal.StatisticalLoggingSessionEventListener=infoWeb
Add Servlet Filter
-
Use
OncePerRequestFilterfor filters to be executed only once for every HTTP request. -
Resources
Serve static content
-
Add
webandthymeleafmodule -
Store static content files under
src/main/resources/static -
Configure static serving paths to file locations
@Configuration @EnableWebMvc public class MvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/js/**").addResourceLocations("classpath:/static/js/"); registry.addResourceHandler("/static/css/**").addResourceLocations("classpath:/static/css/"); registry.addResourceHandler("/static/media/**").addResourceLocations("classpath:/static/media/"); registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); } }
Content Negotiation
Testing
-
Resources
- VMware Tanzu Developer Center - Spring Boot Testing Best Practices (opens in a new tab)
- Guide to @SpringBootTest for Spring Boot Integration Tests (opens in a new tab)
- A Case for Integration Tests: Implementing effective integration tests on Spring Boot – TestContext – The Coders Tower (opens in a new tab)
-
Annotations
@SpringBootTest- Used as an alternative to the standard spring-test
@ContextConfigurationannotation when you need Spring Boot features. The annotation works by creating theApplicationContextused in your tests throughSpringApplication. In addition to@SpringBootTesta number of other annotations are also provided for testing more specific slices of an application.
- Used as an alternative to the standard spring-test
@TestConfiguration
-
Helper beans
WebTestClientTestRestTemplate
Testing - Integration
-
Spring Boot reference - Auto-configured sliced testing (opens in a new tab)
-
Test Auto-configuration for sliced testing (opens in a new tab)
Instead of the whole
ApplicationContext, Only the beans required for a particular slice of an application are used when that slice is being tested. -
Use
@MockBeanto replace a bean with a mock@SpringBootTest class MyTests { @MockBean private SomeService service; ...
Testing - Web
-
Enable
@MockMvc@AutoConfigureMockMvc @WebMvcTest(UserVehicleController.class) class MyControllerTests { @Autowired private MockMvc mvc; ... -
@SpringBootTest-
webEnvironment=MOCK- Web layer unit testing with mocked dependencies using
MockMvcandMockBean
- Web layer unit testing with mocked dependencies using
-
webEnvironment=RANDOM_PORT / DEFINED_PORT- Web layer integration testing with real servers using
RestTemplateorWebTestClientas the client (real HTTP requests)
- Web layer integration testing with real servers using
-
-
WebClientis the reactive equivalent ofRestTemplate.WebTestClientis a wrapper ofWebClientfor testing purposes. -
MockMvcallows you to testSpring MVCcontroller endpoints without realHTTPcommunications (a mocked Servlet environment). -
Use
WireMockto mock unneededHTTPservices. -
Resources
Mockito
Mock a generic type object
-
Use
@Mockannotation to mock a generic type object, the mock will be populated with Spring Boot test support@Mock private ListenableFuture<SendResult<String, Log>> listenableFuture; @Test void publish() throws Exception { var logJson = """ { "id": "%s", "message": "test", "loggedAt": "2021-01-01T00:00:00", "addedAt": "2021-01-01T00:00:00" } """.formatted(UUID.randomUUID()); when(kafkaTemplate.sendDefault(any(Log.class))).thenReturn(listenableFuture); mvc.perform(post("/log/publish/v1") .contentType(APPLICATION_JSON) .content(logJson)) .andExpect(status().isOk()); }
Development
Spring Boot DevTools
-
Tips
- With
Spring Boot DevToolsenabled, the application will restart automatically when a file in the classpath is changed. This is useful for development but should be disabled in production. - In IntelliJ IDEA, when the application is running, make code changes and hit
Ctrl + Shift + F9will compile the source code file and trigger an automatic restart.
- With
Deployment
Change the Name of Project and Spring Boot JAR
- Create a
settings.gradle(orsettings.gradle.kts) under the project directory if it doesn't exist - Add
rootProject.name = '<Desired Project Name>'to it
Convert a Spring Boot executable JAR to a deployable WAR File
Containerization
systemd
-
Resources
Observability
Actuator
-
Resources
-
docs.spring.io - Spring Boot Actuator Web API Documentation (opens in a new tab)
All
Actuatorendpoints reference
Actuator - Implementing Custom JMX Endpoints
-
Notes
- If you add a
@Beanannotated with@Endpoint, any methods annotated with@ReadOperation,@WriteOperation, or@DeleteOperationare automatically exposed overJMXand, in a web application, overHTTPas well. - For reference, check any Spring bean annotated with
@Endpoint, such asHealthEndpoint.
- If you add a
Actuator - Examine beans at runtime
-
Use
Actuatorendpoint:/actuator/beans-
Use
jqcurl -s -G http://<ROOT>/actuator/beans | jq '.contexts[].beans | keys' -
Use VS Code to traverse the JSON response
curl -s -G http://<ROOT>/actuator/beans | jq | code -n -This will open a new VS Code window. Use
Ctrl + Shift + Oto navigate
-
Actuator - List all bean names
curl -s -G http://$CONTEXT_PATH/actuator/beans | jq '.contexts[].beans | keys' | grep -Eo '\w+'Actuator - List all bean classes
curl -s -G http://$CONTEXT_PATH/actuator/beans | jq '.contexts[].beans[].type' | grep -Eo '(\w+\.?\$*/?)+' | sortActuator - Examine loggers at runtime
-
Use
Actuatorendpoint:/actuator/loggersAll loggers and their logging level are listed. Check the package you are interested in.
Actuator - Check if auto configurations are loaded
-
Use
Actuatorendpoint:/actuator/conditionsAll auto configuration classes are listed with their condition evaluation result.
Actuator - List all loaded PropertySource
-
Use
Actuatorendpoint:/actuator/envcurl -s -G http://localhost:8080/actuator/env | jq '.propertySources[].name'
Actuator - List all system properties
-
Use
Actuatorendpoint:/actuator/envcurl -s -G http://localhost:8080/actuator/env | jq '.propertySources[] | select(.properties != null) | .properties' | jq -s add- Defaults only display values
- Non-defaults also display
originof the value
Actuator - Examine all web mappings
-
Use
Actuatorendpoint:/actuator/mappingscurl -s -G http://localhost:8080/actuator/mappings | jq '.contexts|..|.mappings | select(. != null)' -
List all URL patterns via
dispatcherServletcurl -s -G http://localhost:8080/actuator/mappings | jq '..|.patterns? | select(. != null)' | jq -s add | jq '. | unique'
Actuator - List all loaded beans at runtime
/actuator/beans
Actuator - List all loaded config properties at runtime
/actuator/env
Actuator - Enable all Actuator endpoints
- Set
management.endpoints.web.exposure.include=*
Actuator - List all Actuator endpoints
- Set
management.endpoints.web.discovery.enabled=true, and access/actuatorendpoint
Actuator - Profiling application startup
-
Application class
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication application = new SpringApplication(Application.class); application.setApplicationStartup(new FlightRecorderApplicationStartup()); application.run(args); } } -
Ensure the Spring config properties are set as below
management.endpoints.web.exposure.include=startup management.endpoint.startup.enabled=true -
Run the applicationTurn with VM flag
-XX:StartFlightRecording=duration=30s,settings=profile,filename=startup.jfr(Java 11+) -
The
JFRrecording file will be saved under the directory where the Java application is run. When running the application, you may see info similar to shown below in the stdout:Started recording 1. The result will be written to: C:\Users\Takechiyo\workspace\gitlab\startup.jfr -
Open it with
JDK Mission Control, and navigate toEvent Browser->Spring Application->Startup Step, and you will see all the recorded events.
Actuator - List all metrics
-
Use
/actuator/metricsendpoint to list all available metrics -
Use
/actuator/metrics/{metricName}endpoint to get the value of a specific metric -
Metrics
-
Application Info
application.ready.timeapplication.started.time
-
Disk Metrics
disk.freedisk.total
-
Executor Metrics
executor.activeexecutor.completedexecutor.pool.coreexecutor.pool.maxexecutor.pool.sizeexecutor.queue.remainingexecutor.queued
-
HTTP Metrics
http.server.requestshttp.server.requests.active
-
JVM Metrics
-
Memory & Buffers
jvm.buffer.countjvm.buffer.memory.usedjvm.buffer.total.capacityjvm.memory.committedjvm.memory.maxjvm.memory.usage.after.gcjvm.memory.used
-
Garbage Collection
jvm.gc.live.data.sizejvm.gc.max.data.sizejvm.gc.memory.allocatedjvm.gc.memory.promotedjvm.gc.overheadjvm.gc.pause
-
Classes & Compilation
jvm.classes.loadedjvm.classes.unloadedjvm.compilation.time
-
Threads
jvm.threads.daemonjvm.threads.livejvm.threads.peakjvm.threads.startedjvm.threads.states
-
Logging
log4j2.events
-
General
jvm.info
-
-
Process Metrics
process.cpu.timeprocess.cpu.usageprocess.files.maxprocess.files.openprocess.start.timeprocess.uptime
-
Resilience4j Metrics
-
Bulkhead
resilience4j.bulkhead.active.thread.countresilience4j.bulkhead.available.concurrent.callsresilience4j.bulkhead.available.thread.countresilience4j.bulkhead.core.thread.pool.sizeresilience4j.bulkhead.max.allowed.concurrent.callsresilience4j.bulkhead.max.thread.pool.sizeresilience4j.bulkhead.queue.capacityresilience4j.bulkhead.queue.depthresilience4j.bulkhead.thread.pool.size
-
Circuit Breaker
resilience4j.circuitbreaker.buffered.callsresilience4j.circuitbreaker.callsresilience4j.circuitbreaker.failure.rateresilience4j.circuitbreaker.not.permitted.callsresilience4j.circuitbreaker.slow.call.rateresilience4j.circuitbreaker.slow.callsresilience4j.circuitbreaker.state
-
Rate Limiter
resilience4j.ratelimiter.available.permissionsresilience4j.ratelimiter.waiting_threads
-
Retry & Time Limiter
resilience4j.retry.callsresilience4j.timelimiter.calls
-
-
Spring Integration
spring.integration.channelsspring.integration.handlersspring.integration.sourcesspring.kafka.template
-
System Metrics
system.cpu.countsystem.cpu.usagesystem.load.average.1m
-
Actuator - Cache TTL troubleshooting
OpenTelemetry
Security
SSL/TLS certificate
-
The entry in the Keystore must be a
PrivateKeyEntryinPKCS12format or aKeyEntryinJKSformat, both containing a private key and corresponding certificate chain. Checksun.security.provider.JavaKeyStorefor entry types.# SSL server.port=8443 server.ssl.key-store=${keystore.p12} server.ssl.key-store-password=${EXPORT_PASSWORD} # Use: keytool -list -storetype PKCS12 -keystore ${keystore.p12} -storepass $EXPORT_PASSWORD -v | grep 'Alias name' # Keystore entry alias server.ssl.key-alias=${alias_name} # JKS or PKCS12 server.ssl.keyStoreType=PKCS12 # Spring Security security.require-ssl=true
Logging
-
org.springframework.boot.logging.logback.LogbackLoggingSystem -
org.springframework.boot.logging.logback.defaults.xmlDefault
logbackconfiguration XML file -
org.springframework.boot.logging.logback.DefaultLogbackConfigurationDefault
logbackconfiguration used bySpring Boot. -
org.springframework.boot.logging.logback.ColorConverterColor output converter
-
org.springframework.boot.logging.logback.LogbackConfiguratorAllows programmatic configuration of logback which is usually faster than parsing XML.
-
Use environment variable or system property at runtime to change logging level of specific packages
No source code changes needed, therefore no build and deployment, suitable for troubleshooting in any environment
-
Environment variable
Non-intrusive, only need to restart Spring Boot application to pick up the environment variable, best for production troubleshooting
export LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_BOOT_CONTEXT_CONFIG=trace -
System property
Need to modify the CLI used to run the Spring Boot application by attaching the system property in the following form
-Dlogging.level.org.springframework.boot.context.config=trace -
IntelliJ IDEA
- Open
Run/Debug Configurations, and set the property inOverride configuration properties - Rerun the Spring Boot application in
IntelliJ.
- Open
-
-
Resources
Building
Spring Boot Maven Plugin
Spring Boot Gradle Plugin
Build a containerized native image
-
Spring Boot Docs - Reacting to the GraalVM Native Image Plugin (opens in a new tab)
Using
gradle bootBuildImageto build a containerized native image
Add custom JVM flags
-
build.gradlebootRun { jvmArgs = [ '-Dspring.profiles.active=dev', '-Dspring.config.location=classpath:/application-dev.properties' ] } -
build.gradle.ktsproject.tasks.withType<BootRun> { jvmArgs( "-XX:+DisableExplicitGC", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:InitialHeapSize=4g" ) }
Troubleshooting
General method
- Use logging
- Identify the Spring classes to be debugged
- turn on
TRACElogging level for the package of the Spring classes
- Use IDE debugging
- Identify the Spring classes to be debugged
- Use IntelliJ's "evaluate and log" function of debugger to print values
- To make step 2 easier, encapsulate the logic in a custom debugging util class such as IntellijDebugUtil.java (opens in a new tab)
Database table name does not match what is defined by JPA @Table annotation
- Because Spring Boot uses its own naming strategy (
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategystarting fromSpring Boot 1.4) to generate database table names - Stack Overflow - Spring Boot + JPA : Column name annotation ignored (opens in a new tab)
Spring CLI
-
spring init --listTo list all the capabilities of the service
Gradle
Specify system properties on command line
gradle bootRun --args='--spring.profiles.active=dev --spring.config.location=classpath:/application-dev.properties'Kubernetes
Graceful shutdown
- Anatomy
- Receive SIGTERM or PreStop Lifecycle Hook
- Fail ReadinessProbe
- Serve requests until Kubernetes detects ReadinessProbe failure
- Pod Endpoint removed from Service
- Finish serving in-flight requests
- Shutdown
Open Questions
- How to load DB initialization scripts in different profile conditionally?
Resources
- docs.spring.io - Production-ready Features (opens in a new tab)
- docs.spring.io - Deploying Spring Boot Applications (opens in a new tab)
- Spring Blog - Spring Tips: Configuration (opens in a new tab)
- GitHub - spring-projects/spring-boot - Wiki - Release Notes (opens in a new tab)