Code Review Videos > Kotlin > How To Use application.properties in Spring Boot

How To Use application.properties in Spring Boot

This is a post aimed at absolute beginners to Kotlin Spring Boot projects, who maybe have joined an existing team / project that uses this setup, and are unsure how best to set up their local environment using the application.properties resources.

Or, to put it another way, I joined a project that is using Spring Boot / Kotlin, and my initial approach when using application.properties was to start whacking in my own values into the wrong files.

It took a helpful colleague to point out the errors of my ways, and so this is what I have now learned after chatting through that approach, and doing further reading on the subject.

To make this nice and easy to follow along with, we will make use of a demo project – quickly and easily spun up using Spring Initializr:

kotlin spring boot spring initializr

The changes to the defaults are:

  • “Gradle – Kotlin” as the project type
  • “Kotlin” as the language
  • “Spring Web” as the only project dependency
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
	id("org.springframework.boot") version "3.0.5"
	id("io.spring.dependency-management") version "1.1.0"
	kotlin("jvm") version "1.7.22"
	kotlin("plugin.spring") version "1.7.22"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<KotlinCompile> {
	kotlinOptions {
		freeCompilerArgs = listOf("-Xjsr305=strict")
		jvmTarget = "17"
	}
}

tasks.withType<Test> {
	useJUnitPlatform()
}Code language: Kotlin (kotlin)

I’m fairly certain the Java version and all the other settings won’t make any difference to this approach.

That should give you a demo.zip file to download, which you … download, and then unzip. Magical.

What Is The Purpose Of Spring Boot’s application.properties File?

In Spring Boot, the application.properties file is a configuration file that allows you to define various configuration properties for your application. These properties can be used to configure various aspects of your application, such as the database connection details, logging settings, server settings, and more.

The application.properties file is located in the src/main/resources directory of your Spring Boot project, and it is loaded automatically by Spring Boot at startup. You can also use a YAML file (application.yml) instead of a properties file to define the configuration properties.

Here are some examples of configuration properties that can be defined in the application.properties file:

  • Database connection details, such as the URL, username, and password
  • Server port and address
  • Logging settings, such as log levels and appenders
  • Threading settings, such as the number of worker threads
  • External API keys and secrets
  • Various application-specific settings

By defining these configuration properties in the application.properties file, you can easily modify the behaviour of your application without changing the code. Additionally, you can override specific properties by defining them in an external properties file or by passing them in as command-line arguments. This makes it easy to configure your application for different environments (e.g., development, testing, production) or to provide customisation options for your users.

What Is The Precedence / Ordering Of Loading Spring Boot Configuration Files?

In Spring Boot, there is a predefined order of precedence for the configuration files, which determines how conflicting configuration properties are resolved. The order of precedence is as follows:

  1. Command line arguments
  2. SPRING_APPLICATION_JSON environment variable
  3. Servlet context parameters
  4. JNDI attributes
  5. Java System properties (e.g., -D command line arguments)
  6. OS environment variables
  7. application.properties or application.yml in the classpath
  8. application-{profile}.properties or application-{profile}.yml in the classpath

In other words, the highest precedence is given to command line arguments, and the lowest precedence is given to the application-{profile}.properties or application-{profile}.yml files in the classpath.

If a configuration property is defined in multiple files with different precedence levels, then the property with the highest precedence takes precedence. For example, if a property is defined in both the application.properties file and the Java System properties, then the value from the Java System properties will be used.

You can also use the spring.config.name and spring.config.location properties to customise the configuration file names and locations, respectively.

By default, Spring Boot looks for the application.properties or application.yml files in the classpath.

However, you can specify additional locations or filenames by setting the spring.config.location property to a comma-separated list of directory locations or filenames, or by setting the spring.config.name property to a comma-separated list of filenames.

What Are JNDI Attributes

JNDI stands for Java Naming and Directory Interface, which is a standard API in Java that provides a unified interface for accessing various naming and directory services. JNDI allows Java applications to look up and access objects in a naming and directory service, such as a directory server or a naming service.

In the context of Spring Boot, JNDI attributes refer to configuration properties that are defined as attributes in a JNDI context. For example, you can define database connection properties, such as the URL, username, and password, as JNDI attributes and then look them up in your Spring Boot application using the JNDI API.

To define JNDI attributes, you typically use a JNDI provider or a JNDI-based configuration tool, such as Apache Tomcat, JBoss, or WebSphere. You can then look up the JNDI attributes in your Spring Boot application using the JndiObjectFactoryBean or JndiLocatorDelegate classes.

By defining configuration properties as JNDI attributes, you can separate the configuration from the application code and make it easier to manage and maintain. Additionally, JNDI provides a standard way of accessing configuration properties across different Java environments, such as Java EE application servers and Java SE environments.

Basic Project Setup

If you follow along with the official tutorials from Jetbrains on setting up a Kotlin based Spring Boot app, they will show you an approach to creating a Rest controller that returns some JSON where they place that controller code right inside the DemoApplication.kt file.

This obviously works, but if we want to more closely mimic what we would see in a larger project, it makes sense to define our controller in its own file.

kotlin spring boot controller example

With that you should be able to boot / run your app by clicking the green play button in IntelliJ:

green run button intellij

Which spits out a bunch of console output:

19:13:35: Executing ':DemoApplicationKt.main()'...

Starting Gradle Daemon...
Gradle Daemon started in 395 ms
> Task :processResources UP-TO-DATE
> Task :compileKotlin
> Task :compileJava NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar
> Task :inspectClassesForKotlinIC

> Task :DemoApplicationKt.main()

.   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
'  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::                (v3.0.5)

2023-02-29T19:13:42.403+01:00  INFO 207002 --- [           main] com.example.demo.DemoApplicationKt       : Starting DemoApplicationKt using Java 17.0.2 with PID 207002 (/home/chris/Development/spring-boot-kotlin-profile-testing/build/classes/kotlin/main started by chris in /home/chris/Development/spring-boot-kotlin-profile-testing)
2023-02-29T19:13:42.405+01:00  INFO 207002 --- [           main] com.example.demo.DemoApplicationKt       : The following 1 profile is active: "chris"
2023-02-29T19:13:42.736+01:00  INFO 207002 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-02-29T19:13:42.740+01:00  INFO 207002 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-02-29T19:13:42.740+01:00  INFO 207002 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.7]
2023-02-29T19:13:42.775+01:00  INFO 207002 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-02-29T19:13:42.776+01:00  INFO 207002 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 343 ms
2023-02-29T19:13:42.907+01:00  INFO 207002 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-02-29T19:13:42.911+01:00  INFO 207002 --- [           main] com.example.demo.DemoApplicationKt       : Started DemoApplicationKt in 0.661 seconds (process running for 0.841)
2023-02-29T19:13:56.249+01:00  INFO 207002 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-02-29T19:13:56.250+01:00  INFO 207002 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-02-29T19:13:56.250+01:00  INFO 207002 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 msCode language: Shell Session (shell)

And it should be accessible on localhost port 8080 by default:

Hardly the world’s most exciting thing, but nevertheless a lot is going on behind the scenes to make this come together.

It’s worth a quick look at the code to cover one thing I got wrong initially that taught me a bit about how Spring Boot is working. Here’s the controller code directly:

package com.example.demo.controllers

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class MyController {

    @GetMapping("/")
    fun index() = "Hello!"
}Code language: Kotlin (kotlin)

The package declaration is important in a Spring Boot application. The package name determines the base package that Spring Boot will scan to discover components such as controllers, services, repositories, and configuration classes.

By convention, Spring Boot will scan the package and its sub-packages where the @SpringBootApplication annotation is located. This annotation is commonly used to bootstrap a Spring Boot application, and is usually located in the main class of the application.

For example, if your main class is located in the package com.example.demo, Spring Boot will automatically scan this package and its sub-packages for components to configure and manage.

If you place your MyController class in a different package, Spring Boot will not automatically discover it unless you specify the package to scan using @ComponentScan annotation or other related annotations.

My mistake was to incorrectly set the package path. So whilst the app booted, my controller was inaccessible. A bit of a head scratcher for a newbie.

Using application.properties

OK, so that’s the setup out of the way. On with the main point of this post.

If creating a brand new project, the application.properties file will be created, but empty.

You can find this file at the path:

src/main/resources/application.properties

However, on a working project this file likely contains several defined properties.

Crucially, this is not the place to change those values.

A Real World(ish) Example To Get Us Started

These are application defaults, or definitions to things that either must be there, or are expected to be provided by you / the running configuration.

How these are used depends on the code, as best I can tell. The two ways I have seen these values used is either in annotations directly, or injected via annotations.

  @Bean("someBean")
  @ConditionalOnProperty(name = ["some.property"], havingValue = "some-condition")
  open fun myFunService(
    @Value("\${chris.test.key}") myUsefulData: String
  ): SomeUsefulClass {
    return someKindOfConfiguration(myUsefulData).build()
  }Code language: CSS (css)

The annotations @ConditionalOnProperty and @Value are part of the Spring Framework and are used to conditionally instantiate and configure beans in a Spring application context.

The @ConditionalOnProperty annotation is used to conditionally enable a bean definition based on the existence of a certain property in the application configuration file.

The annotation takes two attributes: name and havingValue.

The name attribute specifies the name of the property to check, while the havingValue attribute specifies the value that the property must have in order for the bean definition to be enabled.

In this example, the bean definition for myFunService will only be enabled if the property some.property has the value some-condition.

The @Value annotation is used to inject a value from the application configuration file into a bean property or method parameter.

The annotation takes a single attribute: the SpEL (Spring Expression Language) expression that resolves to the desired value.

In this example, the value of the property chris.test.key is injected into the myUsefulData parameter of the myFunService method.

Together, these annotations allow us to conditionally instantiate and configure a bean based on a specific property in the application configuration file, and to inject values from the same file into the bean’s properties or methods.

A More Basic Example

I always like to have some sort of real world problem to tie my learning back too. It just makes things click so much more easily when I can see how this basic approach might actually apply back in the real world.

Anyway, now that I have my project running, let’s look at increasingly more useful ways to incorporate application.properties into our project.

First up, we need to actually define a new application property.

# src/main/resources/application.properties

chris.test=my stuff goes hereCode language: PHP (php)

Your IDE might highlight this property as not actually being used right now:

intellij cannot resolve configuration property

That’s actually pretty interesting.

Cannot resolve configuration property 'chris.test' Code language: JavaScript (javascript)

This error message means that IntelliJ was unable to find the property chris.test in any of the files in the project.

So IntelliJ is clever enough to look through the project and try and track down where that property is being used. At the moment, that’s nowhere. So it’s helping us out – somehow, we messed up.

Next, let’s actually use this property.

package com.example.demo.controllers

import org.springframework.beans.factory.annotation.Value
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class MyController {

    @Value("\${chris.test}")
    private lateinit var chrisTest: String

    @GetMapping("/")
    fun index() = "Hello, $chrisTest!"
}
Code language: Kotlin (kotlin)

Let’s break it down line by line, starting with:

    @Value("\${chris.test}")
    private lateinit var chrisTest: StringCode language: Kotlin (kotlin)
  • @Value("\${chris.test}") is an annotation that injects a property value from the application configuration file into the variable chrisTest. Specifically, it injects the value of the property chris.test from the configuration file, which is denoted by the ${chris.test} syntax within the annotation’s argument.
  • private lateinit var chrisTest: String is a variable declaration that declares a private variable named chrisTest of type String, which is annotated with lateinit. lateinit is used to indicate that the variable will be initialised at a later time and is not null.

And now the remaining two lines:

    @GetMapping("/")
    fun index() = "Hello, $chrisTest!"Code language: Kotlin (kotlin)
  • @GetMapping("/") is an annotation that maps the HTTP GET request with the path / to the index() function.
  • fun index() = "Hello, $chrisTest!" is a function that returns a string literal that includes the value of chrisTest. Specifically, it returns the string "Hello, " concatenated with the value of chrisTest interpolated with the $ character.

If we boot the app now, we should be able to see this coming together:

Overriding Spring Boot Application Properties

I said back at the start that the wrong thing to do is to define your own values directly in application.properties.

And then we covered the varying orders of precedence in which our application’s configuration is determined.

The settings in application.properties are there to get us started, and set some values that may be standard across any environment / set up.

However, some are meant to be overriden with more specific configuration. For example a database password that might be set to “password” in your local dev, but something far stronger for pre-prod, and production environments.

There’s several ways I’ve seen this done, so let’s cover off each. I’m guessing these are the common ones, but I may have missed some.

Property Placeholders

One thing I’ve seen done is to provide a default value using the property placeholder syntax.

chris.test=${SOMETHING_CUSTOM:my stuff goes here}

The ${SOMETHING_CUSTOM:my stuff goes here} syntax is known as property placeholder syntax, and it allows you to define a default value for the property if it is not defined in any of the configuration files or environment variables.

The expression ${SOMETHING_CUSTOM:my stuff goes here} is evaluating the value of a property called SOMETHING_CUSTOM. If the property SOMETHING_CUSTOM is not defined, then the default value my stuff goes here will be used instead.

For example, if you have a different environment-specific value for the chris.test property, you could define an environment variable called SOMETHING_CUSTOM and set its value to the appropriate value for that environment. Spring Boot will then use the value of the environment variable for the chris.test property.

Spring Boot won’t look for an environment variables file. This caught me out. I thought all I would need to do is define a .env file, and somehow that would magically work.

Not so.

But with IntelliJ it is possible to define custom environment variables in your run config to make use of this property placeholder approach.

Start by editing the configuration for your app:

intellij spring boot edit running configuration

Then edit the Environment Variables section:

intellij edit environment variables

Enter in your custom environment variables as needed:

intellij custom environment variables setup

Click OK, and you should see the section filled in:

intellij setup with custom environment variables

So now if you start the app, you should see the override in place:

spring boot app with overridden application property

If you stop the app, and either change the environment variable name in the IntelliJ run config window, or you change the variable name in your application.properties, e.g.

chris.test=${DIFFERENT_THING:stuff goes here}

Then as DIFFERENT_THING isn’t set, the app falls back to the placeholder value of “stuff goes here”.

Now, that’s one way.

It may not necessarily by the way. So here’s another, and probably the one you should use.

application-{profile}.properties Approach

It’s probably a good idea to create, define, and use your own application-{profile}.properties file.

An example might be application-local-dev.properties.

The name is important.

Whatever part comes in the {profile} bit is needed when telling Spring Boot to use that file during execution.

So, let’s change things up a bit:

# src/main/resources/application.properties

chris.test=my stuff goes hereCode language: PHP (php)

I’ve reverted back for simplicity.

And then the new file:

# src/main/resources/application-local-dev.properties

chris.test=I want this to show instead Code language: PHP (php)

OK, so let’s pretend we have our main file that we use in prod. That’s application.properties for simplicity.

And then we have this custom application-local-dev.properties file (which could also be a .yml file) that we use in local dev.

That means in this case, the profile name is local-dev.

Important: This won’t just work ™.

Which is to say if we boot up the app right now we see “my stuff goes here”, not “I want this to show instead”.

This makes sense, based on the order of precedence we covered earlier.

So how can we make this work?

We need to go back to the run config:

spring boot run config

And set our active profile(s):

spring boot active profile

Which works as expected:

spring boot running profile with override

Though that does raise the question: how?

We literally just said that the values from application.properties are more important than the values from application-{profile}.properties, and then somehow overruled that.

And this comes down to the way in which Spring Boot is started from the command line:

spring boot command line start up with profile

Very fortunately, IntelliJ is figuring out / managing the horrendously long command line command necessary to boot our app properly.

Command line arguments are #1 on the list of precedence in which ‘stuff’ is figured out. So by providing our active profile here, it is used above everything else.

We could even provide a third file:

# src/main/resources/application-another.properties

chris.test=now show thisCode language: PHP (php)

And specify this one also:

spring with two app profiles active

And now the front end uses the value from the last profile it finds that matches:

second profile override value spring boot

Wrapping Up

That’s all I know, so far, with regards to setting up Spring Boot application properties.

I’m sure there is a ton more to learn, but I thought I’d document what I’d found out up to now to cement my knowledge.

As ever, feel free to leave a comment and let me know if there’s anything I can improve. And I hope this helps other fellow Spring Boot newbies on there way. So much to learn.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.