CodeGym /Courses /Spring Boot /SPRING_APPLICATION_JSON: properties from JSON

SPRING_APPLICATION_JSON: properties from JSON

Spring Boot
Level 14, Lesson 2
Available

1. When JSON is more convenient than environment variables

Imagine that you start catalog-service not “as usual”, but in a small demo mode. You want to quickly change the catalog title, enable maintenance mode (maintenance), reduce the featured-course limit, and, for example, move the port so it does not conflict with another service on your laptop. Technically, all of this is just ordinary properties. Practically, typing them by hand one by one gets tedious.

The three launch-time channels are already familiar: env vars, -D, and --key=value. But as soon as you need to pass not one property, but a batch of related values, sending them one by one becomes inconvenient. That is where the JSON block appears: not a new configuration model, but a more compact override on top of the already familiar map.

To feel the pain, it is enough to look at this launch. It works, but if you made a mistake in a single character, the application will simply ignore the wrong key without complaint and act as if everything was intended that way anyway (and then you spend two hours arguing with reality).

# Long launch: many separate environment variables
APP_CATALOG_TITLE="Demo Catalog" \
APP_CATALOG_MAINTENANCE_MODE=true \
APP_CATALOG_MAX_FEATURED_COUNT=1 \
SERVER_PORT=9095 \
./gradlew bootRun

And this is where a simple engineering thought appears: “Can I pass this in one block so it is atomic and in one place?” Spring Boot answers: “Yes. Here is SPRING_APPLICATION_JSON.”

2. What SPRING_APPLICATION_JSON is

It is important to understand the idea correctly right away: SPRING_APPLICATION_JSON is not a “JSON config instead of YAML.” It is a way to pass multiple properties as a single string, most often through an environment variable. Spring Boot can take that string, parse the JSON, and add the resulting values to Environment as a separate property source.

In practice, the mechanism has two “entry points” that are useful to know by name so you do not get confused by documentation and other people’s examples:

  • SPRING_APPLICATION_JSON — the name of the environment variable (environment variable) where you put the JSON.
  • spring.application.json — the same thing in meaning, but as a regular property name (you can pass it both as -Dspring.application.json=... and as --spring.application.json=...).

The trick is that Boot does not just read this value “as a string.” It turns it into a separate property source (roughly: a “JSON source”) and places it high in the priority chain.

To make it clear where the JSON source fits into the familiar ladder, here is a simple precedence diagram that works well for this topic:

flowchart TD
    A["command-line args --key=value"] --> B["SPRING_APPLICATION_JSON spring.application.json"]
    B --> C["Java system properties -Dkey=value"]
    C --> D["OS environment variables"]
    D --> E["external config files"]
    E --> F["packaged application.yaml"]

This diagram is not about “all possible sources in the world,” but about a practical map: where the default is, where the override is, and why one value beats another.

3. Flattening JSON into keys

From a human perspective, JSON is a tree (nested objects). From the perspective of Environment, it is flat keys like app.catalog.title. So Boot does something straightforward: it takes the nesting and flattens it into dot-separated keys.

For example, this JSON:

{
  "app": {
    "catalog": {
      "title": "JSON title",
      "maintenance-mode": true
    }
  }
}

will turn into two ordinary properties:

- app.catalog.title = JSON title
- app.catalog.maintenance-mode = true

You can think of it as YAML, just without pretty indentation and without comments. (Yes, that is a downside. JSON does not tolerate comments. It is strict like an instructor at an exam — “extra comma? goodbye.”)

A good way to reinforce the idea is a small correspondence table:

JSON fragment What it becomes in Environment
{"server":{"port":9095}}
server.port=9095
{"app":{"catalog":{"title":"Demo"}}}
app.catalog.title=Demo
{"app":{"catalog":{"maintenance-mode":true}}}
app.catalog.maintenance-mode=true

Pay special attention to types. In JSON true is a boolean value, while "true" is a string. For Environment, in most cases this will still look like a string, but when you try to retrieve the value with type conversion, the difference may become noticeable. And, more importantly, the difference is noticeable to you as a person: "true" looks like “I am not sure what I am doing, but let it be in quotes just in case.” Quotes in configuration rarely add confidence.

4. Passing JSON: env, -D, --

At the day-to-day level, SPRING_APPLICATION_JSON has three “transports.” No new precedence system appears here: only the way the same string reaches Boot changes. And yes, this is where the fun begins: escaping quotes. Think of it as a small tax on compactness.

Through env var SPRING_APPLICATION_JSON

In Unix-like shells, it is usually easiest to use single quotes on the outside and leave double quotes inside the JSON:

# One JSON block instead of a set of separate environment variables
SPRING_APPLICATION_JSON='{"app":{"catalog":{"title":"Demo Catalog","maintenance-mode":true}}}' \
./gradlew bootRun

If you are using Windows PowerShell, the approach is similar, only the assignment syntax is different:

# PowerShell: set the environment variable and run the application
$env:SPRING_APPLICATION_JSON = '{"app":{"catalog":{"title":"Demo Catalog","maintenance-mode":true}}}'
./gradlew bootRun

The main idea: we pass one variable, but inside it there are several related settings at once.

Through -Dspring.application.json=...

Sometimes it is more convenient to pass this as a JVM parameter, especially if you run the application as a normal Java command and want the setting to live at the JVM level (or if it is easier to configure that way in the IDE).

The example looks like this:

# Important: -D... is a JVM parameter, and it must come before -jar
java -Dspring.application.json='{"server":{"port":9095}}' -jar app.jar

Here is the important point from the “mistake of a million” category: -D... is a JVM parameter, and it must come before -jar. If you place it after, the JVM will not see it. It will not be offended, it will simply ignore it — and you will be arguing with reality again.

Through --spring.application.json=...

Boot can accept this as an application argument too. On the surface, it looks exactly like a regular --key=value, except the value is JSON:

# Gradle: application arguments are passed through --args (and this is where the quotes usually begin)
./gradlew bootRun --args='--spring.application.json={"app":{"catalog":{"title":"CLI JSON title"}}}'

It works, but it is often the most quote-dependent option. If you do not enjoy escaping quotes and arguing with the shell, an env var is usually calmer.

5. Priorities and null

SPRING_APPLICATION_JSON is easy to overestimate: you set APP_CATALOG_TITLE, it does not work, and then it turns out there was a JSON block above it. So here it is useful to fix not the whole map again, but only this source’s place: command-line args are usually stronger, then comes the JSON block, then system properties and env vars, and then files.

Let us examine one key, app.catalog.title, in five places to see the behavior with our own eyes:

1) application.yaml contains a default:

app:
  catalog:
    # Default value from the config file
    title: "Title from file"

2) You added an env var:

APP_CATALOG_TITLE="Title from env"

3) You added a JSON block:

SPRING_APPLICATION_JSON='{"app":{"catalog":{"title":"Title from JSON"}}}'

4) And on top of that, you specified command-line args:

--app.catalog.title="Title from CLI"

The final value will be "Title from CLI", because command-line arguments beat everything else.

Now for an important nuance about null. Beginners often expect that this can “reset” a property:

# Important: null does not work here as a reset/delete, but rather as "the key is absent"
SPRING_APPLICATION_JSON='{"app":{"catalog":{"title":null}}}'

Expectation: “Well, I set null, so it must be empty.” Reality: for this mechanism, null does not work as “deleting” the key. As a result, you will keep the value from a weaker source (for example, from application.yaml), because the key with null is essentially treated as absent. In other words, null is not a Reset button.

If you need to get back to the default, the safest way is simply not to set the override. Spring Boot configuration generally likes a simple philosophy: “if you do not know what to do, remove the extra piece, and it will become clearer.”

6. Mini diagnostics through Environment

When source conflicts begin, the brain tries to remember what you launched, what was in the terminal, what the IDE injected into Run Configuration, and where the actual truth is. At that moment, the most useful thing is not to guess, but to ask the application directly: “What do you see in Environment?”

Below is the same temporary probe runner, but with keys that are convenient to check specifically for a JSON override. Do not keep it in the project permanently: it is just a flashlight to quickly see who won in the precedence chain.

import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

// Teaching helper: print the final values from Environment to understand source precedence
@Bean
ApplicationRunner configEchoRunner(Environment env) {
    return args -> {
        // getProperty(...) usually returns a string or null if the key is not set in any source
        System.out.println("app.catalog.title = " + env.getProperty("app.catalog.title"));

        // A key with a dash is normal: it was "flattened" from JSON/YAML into app.catalog.maintenance-mode
        System.out.println("maintenance = " + env.getProperty("app.catalog.maintenance-mode"));

        // A handy marker for checking which source overrode server.port
        System.out.println("server.port = " + env.getProperty("server.port"));
    };
}

If you start the application with this JSON:

SPRING_APPLICATION_JSON='{"app":{"catalog":{"title":"JSON title","maintenance-mode":true}},"server":{"port":9095}}' \
./gradlew bootRun

the output will look roughly like this (values are examples):

app.catalog.title = JSON title          // app.catalog.title = JSON title
maintenance = true                     // maintenance = true
server.port = 9095                     // server.port = 9095

The point here is that the application stops being a “black box.” You are no longer arguing with the YAML file on disk. You ask Environment, and it shows you the final truth.

7. Typical mistakes when using SPRING_APPLICATION_JSON

This topic seems small and “obvious enough” until you hit your first launch where the JSON did not parse, the shell ate the quotes, and the application quietly started with defaults — and you are sure Boot is ignoring you out of spite. In reality, almost all problems here are typical and are solved by paying attention to syntax and priorities. Let us look at the most common pitfalls.

Mistake #1: Invalid JSON (extra comma, unescaped quote, comment).
YAML can be a bit more “human” and sometimes forgives small mistakes, while JSON does not. The config is especially often broken by a trailing comma ({"a":1,}) and attempts to insert comments. The fix is boring: validate the JSON and keep it as small as possible.

Mistake #2: The shell “ate” the quotes, and garbage reached Boot.
On Bash and PowerShell, quote rules are different; on Windows CMD, it is even more entertaining. If you see that the value seems not to apply, the first thing to do is print the environment variable in that same terminal and make sure it really contains a correct JSON string, not a clipped fragment.

Mistake #3: Trying to store a big config in one JSON string.
SPRING_APPLICATION_JSON is good when you override 2–5 related values. When you try to cram half of application.yaml into it, readability disappears, the risk of errors grows, and any change turns into “editing a booby-trapped string.” For permanent configuration, files are still better.

Mistake #4: Expecting null to “reset” a value from a file.
Intuitively, it seems that null means “empty,” so the override should win. But for this mechanism, null does not work as key deletion. In the end, you think you “nulled it out,” but the application takes the value from lower in the precedence chain. If you need to remove an override, remove the override itself.

Mistake #5: Mixing up source priorities, especially CLI args above JSON.
A very common scenario: you set a JSON block and assume it is the main one, but then somewhere in the IDE or launch command there is a hidden --app.catalog.title=.... And it wins. In situations like that, it helps either to print the final values through Environment or simply to be disciplined: in conflict situations, always look at the actual launch command first.

1
Task
Spring Boot, level 14, lesson 2
Locked
Multiple properties through SPRING_APPLICATION_JSON
Multiple properties through SPRING_APPLICATION_JSON
1
Task
Spring Boot, level 14, lesson 2
Locked
null in JSON does not erase the value from the file
null in JSON does not erase the value from the file
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION