So, we’ve decided to pick upSpring Boot and we started adding some of its magical starters. As discussed earlier in this chapter, this will activate a handful ofSpring beans.
Assuming we were building a web app and selected Spring MVC’sspring-boot-starter-web
, it would activate embeddedApache Tomcat as the servlet container of choice. And with that, Spring Boot is forced to make a lotof assumptions.
For example, what port should it listen on? Whatabout the context path?Secure Sockets Layer (SSL)? Threads? There are a dozen other parameters to fire up a Tomcatservlet container.
And Spring Boot will pick them. So, where does that leave us? Are we stuck withthem? No.
Spring Boot introducesconfiguration properties as a way to plug property settings into any Spring bean. Spring Boot may load certain properties with default values, but we have the opportunity tooverride them.
The simplest example is the first property mentioned earlier in this section – theserver port.
Spring Boot launches with a default port in mind, but we can change it. This can be done by first adding anapplication.properties
file to oursrc/main/resources
folder. Inside that file, we must merely addthe following:
server.port=9000
This Java property file, a file format supported since the early days of Java 1.0, contains a list of key-value pairs separated by an equals sign (=
). The left-hand side contains the key (server.port
) and the right-hand side contains thevalue (9000
).
When a Spring Boot application launches, it will look for this file and scan in all its property entries, and then apply them. And with that, Spring Boot will switch from its default port of8080
to port9000
.
Note
The server port property is really handy when you need to run more than one Spring Boot-based web application on thesame machine.
Spring Boot is not limited to the handful of properties that can be applied to embedded with Apache Tomcat. Spring Boot has alternative servlet container starters, includingJetty andUndertow. We’ll learn how to pick and choose servlet containers inChapter 2,Creating a Web Application withSpring Boot.
What’s important is knowing that no matter which servlet container we use, theservlet.port
property will be applied properly to switch the port the servlet will serve webrequests on.
Perhaps you’re wondering why? Having a common port property between servlet containers eases choosingservlet containers.
Yes, there are container-specific property settings if we needed that level of control. But generalized properties make it easy for us to select our preferred container and move to a port and context pathof choice.
But we’re getting ahead of ourselves. The point of Spring Boot property settings isn’t about servlet containers. It’s about creating opportunities to make our applications flexible at runtime. And the next section will show us how to createconfiguration properties.
Creating custom properties
At the beginning of this section, Imentioned that configuration properties can be applied to any Spring bean. This applies not just to Spring Boot’s autoconfigured beans, but to our ownSpring beans!
Look at thefollowing code:
@Component@ConfigurationProperties(prefix = "my.app")public class MyCustomProperties { // if you need a default value, assign it here or the constructor private String header; private String footer; // getters and setters}
The preceding code can be describedas follows:
@Component
is Spring Framework’s annotation to automatically create an instance of this class when the application starts and register it with theapplication context.@ConfigurationProperties
is a Spring Boot annotation that labels this Spring bean as a source of configuration properties. It indicates that the prefix of such properties willbemy.app
.
The class itself must adhere to standard Java bean property rules (described earlier in this chapter). It will create various fields and include proper getters and setters – in this case,getHeader()
andgetFooter()
.
With this class added to our application, we can include our own custom properties,as follows:
application.properties:my.app.header=Learning Spring Boot 3my.app.footer=Find all the source code athttps://github.com/PacktPublishing/Learning-Spring-Boot-3.0
These two lines will be read by Spring Boot and injected into theMyCustomProperties
Spring bean before it gets injected into the application context. We can then inject that bean into any relevant component inour app.
But a much more tangible concept would be including properties that should never be hardcoded into an application,as follows:
@Component@ConfigurationProperties(prefix = "app.security")public class ApplicationSecuritySettings { private String githubPersonalCode; public String getGithubPersonalCode() { return this.githubPersonalCode; } public void setGithubPersonalCode (String githubPersonalCode) { this.githubPersonalCode = githubPersonalCode; }}
The preceding code is quite similar to the earlier code but with thefollowing differences:
- The prefix for this class’s propertiesis
app.security
- The
githubPersonalCode
field is a string used to store an API passcode used to presumably interact with GitHub through itsOAuth API
An application that needs to interact with GitHub’s API will need a passcode to get in. We certainly do not want to bake that into the application. What if the passcode were to change? Should we rebuild and redeploy the whole application justfor that?
No. It’s best to delegate certain aspects of an application to an external source. How can we do that? The next section willshow how!
Externalizing application configuration
Did I mention an external source in the previous section? Yes. That’s because while you can put properties into anapplication.properties
file that gets baked into the application, that isn’t the only way to do things. There are more options when it comes to providing Spring Boot with application properties that aren’t solely insidethe deliverable.
Spring Boot not only looks for thatapplication.properties
tucked inside our JAR file upon startup. It will also look directly in the folder from where we run the application to find anyapplication.properties
files there andload them.
We can deliver our JAR file along with anapplication.properties
file right beside it as an immediate way to override pre-baked properties (ours orSpring Boot’s!).
But wait, there’s more. Spring Boot alsosupportsprofiles.
What are profiles? We can createprofile-specific property overrides. A good example would be one configuration for the development environment, but a different one for our test bed,or production.
In essence, we can create variations ofapplication.properties
, asshown here:
application-dev.properties
is a set of properties applied when thedev
profileis activatedapplication-test.properties
is applied when thetest
profileis appliedapplication.properties
is always applied, so it could be deemed theproduction environment
Perhaps an example isin order?
Imagine having our database connection details captured in a property namedmy.app.databaseUrl
, asshown here:
application.properties:my.app.databaseUrl=https://user:pass@production-server.com:1234/prod/
The test bed of our system surely won’t be linked to the same production server. So, instead, we must provide anapplication-test.properties
with thefollowing override:
application-test.properties:my.app.databaseUrl=http://user:pass@test-server.com:1234/test/
To activate thisoverride, simply include-Dspring.profiles.active=test
as an extra argument to the Java command to runour app.
It’s left as an exercise for you to think up overrides for adevelopment environment.
Note
Since production is the end state of an application, it’s usually best practice to letapplication.properties
be the production version of property settings. Use other profiles for other environmentsor configurations.
Notice earlier how we said Spring Boot will scan eitherapplication.properties
files embedded inside our JAR as well as outside the JAR? The same goes for profile-specificproperty files.
So far, we’ve mentioned internal and external properties, both default and profile-specific. In truth, there are many more ways to bind property settings into a SpringBoot application.
Several are included in this list, ordered from lowest priority tohighest priority:
- Default properties provided by Spring Boot’s
SpringApplication.setDefaultProperties()
method. @PropertySource
-annotated@
Configuration
classes.- Config data (such as
application.properties
files). - A
RandomValuePropertySource
that has properties onlyinrandom.*
. - OSenvironment variables.
- Java systemproperties (
System.getProperties()
). - JNDI attributesfrom
java:comp/env
. ServletContext
init parameters.ServletConfig
init parameters.- Properties from
SPRING_APPLICATION_JSON
(inline JSON embedded in an environment variable orsystem property). - Command-line arguments.
- The
properties
attribute on your tests. This is available with the@SpringBootTest
annotation and also slice-based testing (which we’ll cover later inChapter 5,Testing withSpring Boot). @TestPropertySource
annotations onyour tests.- DevTools global settings properties (the
$HOME/.config/spring-boot
directory when Spring Boot DevToolsis active).
Config files are considered in thefollowing order:
- Application properties packaged inside yourJAR file.
- Profile-specific application properties inside yourJAR file.
- Application profiles outside yourJAR file.
- Profile-specific application properties outside yourJAR file.
It’s a bit of a tangent, but we can also ensure certain beans areonly activated when certain profilesare activated.
And properties aren’t confined to injecting data values. The following section will show you how to makeproperty-based beans.
Configuring property-based beans
Properties aren’t just for providing settings. They can also govern which beans are createdand when.
The following code is a common pattern fordefining beans:
@Bean @ConditionalOnProperty(prefix="my.app", name="video")YouTubeService youTubeService() { return new YouTubeService();}
The preceding code can be explainedas follows:
@Bean
is Spring’s annotation, signaling that the following code should be invoked when creating an application context and the created instance is added as aSpring bean@ConditionalOnProperty
is Spring Boot’s annotation to conditionalize this action based on the existence ofthe property
If we setmy.app.video=youtube
, then a bean of theYouTubeService
type will be created and injected into the application context. Actually, in this scenario, if we definemy.app.video
with any value, it will createthis bean.
If the property does not exist, then the bean won’t be created. This saves us from having to dealwith profiles.
It’s possible to fine-tune this even further, asshown here:
@Bean @ConditionalOnProperty(prefix="my.app", name="video", havingValue="youtube")YouTubeService youTubeService() { return new YouTubeService();}@Bean @ConditionalOnProperty(prefix="my.app", name="video", havingValue="vimeo")VimeoService vimeoService() { return new VimeoService();}
This preceding code can beexplainedas follows:
@Bean
, like before, will define Spring beans to be created and added to theapplication context@ConditionalOnProperty
will conditionalize these beans to only be created if the named property has thestated values
This time, if we setmy.app.video=youtube
, aYouTubeService
will be created. But if we were to setmy.app.video=vimeo
, aVimeoService
bean would becreated instead.
All of this presents a rich way to define application properties. We can create all the configuration beans we need. We can apply different overrides based on various environments. And we can also conditionalize which variants of various services are created based onthese properties.
We can also control which property settings are applicable in a given environment, be it a test bed, a developer’s work environment, a production setting, or a backup facility. We can even apply additional settings based on being in differentcloud providers!
And as a bonus, most modern IDEs (IntelliJ IDEA, Spring Tool Suite, Eclipse, and VS Code) offer autocompletion insideapplication.properties
files! We will cover this in more detail throughout the rest ofthis book.
Now, the last thing we need to craft a powerful application is the means to maintain it. This will be covered in thenext section.