Customizing the setup with configuration properties
So, we’ve decided to pick up Spring Boot and we started adding some of its magical starters. As discussed earlier in this chapter, this will activate a handful of Spring beans.
Assuming we were building a web app and selected Spring MVC’s spring-boot-starter-web
, it would activate embedded Apache Tomcat as the servlet container of choice. And with that, Spring Boot is forced to make a lot of assumptions.
For example, what port should it listen on? What about the context path? Secure Sockets Layer (SSL)? Threads? There are a dozen other parameters to fire up a Tomcat servlet container.
And Spring Boot will pick them. So, where does that leave us? Are we stuck with them? No.
Spring Boot introduces configuration 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 to override them.
The simplest example is the first property mentioned earlier in this section – the server port.
Spring Boot launches with a default port in mind, but we can change it. This can be done by first adding an application.properties
file to our src/main/resources
folder. Inside that file, we must merely add the 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 the value (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 of 8080
to port 9000
.
Note
The server port property is really handy when you need to run more than one Spring Boot-based web application on the same 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, including Jetty and Undertow. We’ll learn how to pick and choose servlet containers in Chapter 2, Creating a Web Application with Spring Boot.
What’s important is knowing that no matter which servlet container we use, the servlet.port
property will be applied properly to switch the port the servlet will serve web requests on.
Perhaps you’re wondering why? Having a common port property between servlet containers eases choosing servlet 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 path of 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 create configuration properties.
Creating custom properties
At the beginning of this section, I mentioned that configuration properties can be applied to any Spring bean. This applies not just to Spring Boot’s autoconfigured beans, but to our own Spring beans!
Look at the following 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 described as follows:
@Component
is Spring Framework’s annotation to automatically create an instance of this class when the application starts and register it with the application 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 will bemy.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()
and getFooter()
.
With this class added to our application, we can include our own custom properties, as follows:
application.properties:
my.app.header=Learning Spring Boot 3
my.app.footer=Find all the source code at https://github.com/PacktPublishing/Learning-Spring-Boot-3.0
These two lines will be read by Spring Boot and injected into the MyCustomProperties
Spring bean before it gets injected into the application context. We can then inject that bean into any relevant component in our 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 the following differences:
- The prefix for this class’s properties is
app.security
- The
githubPersonalCode
field is a string used to store an API passcode used to presumably interact with GitHub through its OAuth 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 just for that?
No. It’s best to delegate certain aspects of an application to an external source. How can we do that? The next section will show how!
Externalizing application configuration
Did I mention an external source in the previous section? Yes. That’s because while you can put properties into an application.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 inside the deliverable.
Spring Boot not only looks for that application.properties
tucked inside our JAR file upon startup. It will also look directly in the folder from where we run the application to find any application.properties
files there and load them.
We can deliver our JAR file along with an application.properties
file right beside it as an immediate way to override pre-baked properties (ours or Spring Boot’s!).
But wait, there’s more. Spring Boot also supports profiles.
What are profiles? We can create profile-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 of application.properties
, as shown here:
application-dev.properties
is a set of properties applied when thedev
profile is activatedapplication-test.properties
is applied when thetest
profile is appliedapplication.properties
is always applied, so it could be deemed the production environment
Perhaps an example is in order?
Imagine having our database connection details captured in a property named my.app.databaseUrl
, as shown 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 an application-test.properties
with the following override:
application-test.properties:
my.app.databaseUrl=http://user:pass@test-server.com:1234/test/
To activate this override, simply include -Dspring.profiles.active=test
as an extra argument to the Java command to run our app.
It’s left as an exercise for you to think up overrides for a development environment.
Note
Since production is the end state of an application, it’s usually best practice to let application.properties
be the production version of property settings. Use other profiles for other environments or configurations.
Notice earlier how we said Spring Boot will scan either application.properties
files embedded inside our JAR as well as outside the JAR? The same goes for profile-specific property 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 Spring Boot application.
Several are included in this list, ordered from lowest priority to highest 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 only inrandom.*
. - OS environment variables.
- Java system properties (
System.getProperties()
). - JNDI attributes from
java:comp/env
. ServletContext
init parameters.ServletConfig
init parameters.- Properties from
SPRING_APPLICATION_JSON
(inline JSON embedded in an environment variable or system 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 in Chapter 5, Testing with Spring Boot). @TestPropertySource
annotations on your tests.- DevTools global settings properties (the
$HOME/.config/spring-boot
directory when Spring Boot DevTools is active).
Config files are considered in the following order:
- Application properties packaged inside your JAR file.
- Profile-specific application properties inside your JAR file.
- Application profiles outside your JAR file.
- Profile-specific application properties outside your JAR file.
It’s a bit of a tangent, but we can also ensure certain beans are only activated when certain profiles are activated.
And properties aren’t confined to injecting data values. The following section will show you how to make property-based beans.
Configuring property-based beans
Properties aren’t just for providing settings. They can also govern which beans are created and when.
The following code is a common pattern for defining beans:
@Bean
@ConditionalOnProperty(prefix="my.app", name="video")
YouTubeService youTubeService() {
return new YouTubeService();
}
The preceding code can be explained as 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 a Spring bean@ConditionalOnProperty
is Spring Boot’s annotation to conditionalize this action based on the existence of the property
If we set my.app.video=youtube
, then a bean of the YouTubeService
type will be created and injected into the application context. Actually, in this scenario, if we define my.app.video
with any value, it will create this bean.
If the property does not exist, then the bean won’t be created. This saves us from having to deal with profiles.
It’s possible to fine-tune this even further, as shown 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 be explained as follows:
@Bean
, like before, will define Spring beans to be created and added to the application context@ConditionalOnProperty
will conditionalize these beans to only be created if the named property has the stated values
This time, if we set my.app.video=youtube
, a YouTubeService
will be created. But if we were to set my.app.video=vimeo
, a VimeoService
bean would be created 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 on these 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 different cloud providers!
And as a bonus, most modern IDEs (IntelliJ IDEA, Spring Tool Suite, Eclipse, and VS Code) offer autocompletion inside application.properties
files! We will cover this in more detail throughout the rest of this book.
Now, the last thing we need to craft a powerful application is the means to maintain it. This will be covered in the next section.