Profiles & Environments

Reviewed & published by Brayan K

Profiles let one build behave differently per environment. Tag beans with @Profile, layer application-<profile>.properties, and flip everything with spring.profiles.active.

Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

Before You Start

You should understand configuration and properties and how dependency injection picks beans. Profiles build directly on both.

What You'll Learn

The Big Idea — One Stage, Different Scenery

💡 Analogy: Think of your app as a theater stage and profiles as scene sets. The same actors (your code) perform on one stage, but a stagehand rolls in the "dev" scenery (a cardboard prop mailer, bright debug lights) or the "prod" scenery (the real backdrop, dimmed lights) depending on the show. spring.profiles.active is the cue that tells the stagehand which set to use; @Profile labels which props belong to which scene.

Build the play once; change the scenery per performance.

1️⃣ Per-Profile Property Files

application.properties is the base; application-dev.properties and application-prod.properties layer overrides on top when their profile is active. Activate one with spring.profiles.active.

# application.properties  (base config, always loaded)
app.name=Bookstore
app.mailer=real

# application-dev.properties  (overrides when the "dev" profile is active)
app.mailer=fake
logging.level.root=DEBUG

# application-prod.properties (overrides when the "prod" profile is active)
app.mailer=real
logging.level.root=WARN

# Activate a profile (any ONE of these):
#   spring.profiles.active=dev                 (in application.properties)
#   --spring.profiles.active=prod              (command-line argument)
#   SPRING_PROFILES_ACTIVE=prod                (environment variable)

2️⃣ Swapping Beans with @Profile

Annotate competing implementations with @Profile. A service depends on the interface; Spring injects whichever bean matches the active profile — no environment if/else in your logic.

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

// A single interface, two implementations chosen by the ACTIVE profile.
public interface Mailer {
    String send(String to, String body);
}

@Component
@Profile("dev")                 // created ONLY when "dev" is active
class FakeMailer implements Mailer {
    public String send(String to, String body) {
        return "DEV (not really sent) -> " + to;
    }
}

@Component
@Profile("prod")                // created ONLY when "prod" is active
class SmtpMailer implements Mailer {
    public String send(String to, String body) {
        return "SENT via SMTP -> " + to;
    }
}

// A service just depends on the interface; Spring injects whichever
// implementation matches the active profile — no if/else in your code.
import org.springframework.context.annotation.*;

// You can also group bean definitions per profile in a @Configuration class.
@Configuration
public class DataSourceConfig {

    @Bean
    @Profile("dev")
    public String devDb() {                 // pretend this is a DataSource
        return "in-memory H2 database";
    }

    @Bean
    @Profile("prod")
    public String prodDb() {
        return "managed Postgres cluster";
    }
}

3️⃣ How Selection Works

Under the hood, Spring just picks the bean whose profile matches spring.profiles.active. Here is that selection logic in plain, runnable Java.

import java.util.*;

// PLAIN-JAVA illustration of "pick the bean for the ACTIVE profile",
// which is what @Profile + spring.profiles.active do for you.
public class Main {

    interface Mailer { String send(String to); }

    static class FakeMailer implements Mailer {
        public String send(String to) { return "DEV (not really sent) -> " + to; }
    }
    static class SmtpMailer implements Mailer {
        public String send(String to) { return "SENT via SMTP -> " + to; }
    }

    // choose the implementation based on the active profile
    static Mailer mailerFor(String activeProfile) {
        return switch (activeProfile) {
            case "prod" -> new SmtpMailer();
            default     -> new FakeMailer();   // dev / default
        };
    }

    public static void main(String[] args) {
        // pretend spring.profiles.active was read from config:
        String active = "dev";
        Mailer mailer = mailerFor(active);
        System.out.println("active profile: " + active);
        System.out.println(mailer.send("ana@site.com"));

        active = "prod";
        System.out.println("active profile: " + active);
        System.out.println(mailerFor(active).send("ben@site.com"));
    }
}

🧠 Quick Recall

Predict before you peek.

Answer: spring.profiles.active=prod (settable via file, CLI arg, or env var).

Answer: application-dev.properties, layered on top of the base file.

Answer: the default profile — base config and any non-profiled beans.

🎯 YOUR TURN — Sandbox vs live gateway

Provide two PaymentGateway implementations chosen by @Profile("dev") and @Profile("prod").

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

// YOUR TURN: provide two implementations of a PaymentGateway.
//  - interface PaymentGateway { String charge(int cents); }
//  - @Profile("dev")  -> SandboxGateway returns "SANDBOX charge " + cents
//  - @Profile("prod") -> LiveGateway returns "LIVE charge " + cents
//  - a @Component on each so Spring picks by active profile
//
// Then set spring.profiles.active=dev to use the sandbox locally.

🧩 MINI-CHALLENGE — Layered property files

Design base and per-profile property files, then activate prod with an environment variable.

# MINI-CHALLENGE: design per-profile property files.
#
# Base (application.properties):
#     app.rate-limit=100
#     app.cache-enabled=true
#
# Provide overrides:
#   application-dev.properties  -> app.rate-limit=1000, app.cache-enabled=false
#   application-prod.properties -> app.rate-limit=100,  logging.level.root=WARN
#
# Then activate prod with an ENVIRONMENT VARIABLE (no file edit):
#     SPRING_PROFILES_ACTIVE=prod
#
# Think: which keys come from the base file vs the profile file when prod is active?

Common Errors

Pro Tips

📋 Quick Reference

ElementPurposeNote
profilenamed environmentdev/test/prod
@Profile("dev")restrict a beanCreated if active
spring.profiles.activeselect profile(s)Comma-separated
application-dev.propertiesper-profile configOverrides base
default profileno profile activeBase + non-profiled
env varSPRING_PROFILES_ACTIVEActivate at launch
CLI arg--spring.profiles.activePer run
@Bean + @Profileprofiled @Bean methodIn @Configuration
multiple activedev,metricsCompose configs

📚 Resources / Keep Learning

🎉 Lesson Complete!

You can now define profiles, restrict beans and @Bean methods with @Profile, layer application-<profile>.properties over the base, and select the active environment with spring.profiles.active via files, CLI args, or environment variables.

Practice quiz

What does a Spring profile represent?

  • A user account
  • A named set of beans and config active in a given environment
  • A database index
  • A logging level only

Answer: A named set of beans and config active in a given environment. A profile is a named grouping of beans and configuration activated for a particular environment (dev, test, prod).

What does @Profile("dev") on a bean mean?

  • The bean is always created
  • The bean is deleted in dev
  • The bean is created only when the dev profile is active
  • The bean runs twice

Answer: The bean is created only when the dev profile is active. @Profile("dev") makes the bean eligible for creation only when the dev profile is active.

Which property selects the active profile(s)?

  • spring.active.profile
  • spring.profiles.active
  • profile.spring.use
  • server.profile

Answer: spring.profiles.active. spring.profiles.active lists the profile(s) Spring should activate.

What file naming convention provides per-profile properties?

  • application-<profile>.properties
  • profile.<name>.properties
  • <profile>.application.properties
  • app.<profile>.yml only

Answer: application-<profile>.properties. application-dev.properties (etc.) supplies profile-specific overrides loaded on top of application.properties.

If no profile is explicitly active, what runs?

  • Nothing
  • Every profile at once
  • Only prod
  • The default profile / non-profiled beans and base config

Answer: The default profile / non-profiled beans and base config. With no active profile, Spring uses the default profile: base config and beans not restricted to a profile.

Can more than one profile be active at the same time?

  • No, only one
  • Yes, you can activate multiple profiles together
  • Only two maximum
  • Only in tests

Answer: Yes, you can activate multiple profiles together. spring.profiles.active accepts a comma-separated list, so several profiles can be active together.

How can you activate a profile at launch without editing files?

  • You cannot
  • Set spring.profiles.active via an env var or --spring.profiles.active arg
  • Rebuild the JAR
  • Rename the main class

Answer: Set spring.profiles.active via an env var or --spring.profiles.active arg. An environment variable or command-line argument can set spring.profiles.active at startup.

A good use of @Profile is to...

  • Provide a fake email sender in dev and a real one in prod
  • Define the primary key
  • Replace validation
  • Speed up the JVM

Answer: Provide a fake email sender in dev and a real one in prod. Profiles let you swap implementations per environment, e.g. a no-op/fake sender in dev versus a real one in prod.

Which is true about per-profile property files?

  • They replace application.properties entirely
  • They override matching keys on top of the base application.properties
  • They are ignored
  • They must be YAML

Answer: They override matching keys on top of the base application.properties. Profile-specific files override only the keys they define, layered on top of the base properties.

Where can @Profile be applied besides a @Component bean?

  • Only on fields
  • On a @Bean method or a whole @Configuration class
  • Only on the main method
  • Nowhere else

Answer: On a @Bean method or a whole @Configuration class. @Profile can also restrict individual @Bean methods or an entire @Configuration class to an active profile.

Continue this course

Frequently asked questions

What is a Spring profile?

A profile is a named environment — like dev, test, or prod — that activates a particular set of beans and configuration. By tagging beans with @Profile and providing profile-specific property files, you can make one build behave differently depending on which profile is active. This lets you, for example, use an in-memory database and verbose logging in development while using a managed database and minimal logging in production, all without changing or rebuilding the code.

How do I activate a profile?

Set the spring.profiles.active property. You can put spring.profiles.active=dev in application.properties, pass --spring.profiles.active=prod as a command-line argument, or set the SPRING_PROFILES_ACTIVE environment variable. The command-line and environment options are preferred for real deployments because they let the same artifact run in any environment without editing files — a container, for instance, just sets the environment variable. You can also list several profiles, comma-separated, to activate them together.

What does @Profile do on a bean or configuration?

@Profile("dev") restricts a bean (or an entire @Configuration class, or a @Bean method) so it is only created when that profile is active. This is the mechanism for swapping implementations per environment: you can have a FakeMailer annotated @Profile("dev") and an SmtpMailer annotated @Profile("prod"), both implementing the same interface. A service simply depends on the interface, and Spring injects whichever implementation matches the active profile — no environment checks scattered through your business logic.

How do profile-specific property files work?

Spring Boot loads application.properties as the base, then layers application-<profile>.properties on top for each active profile. So application-dev.properties only needs to specify the keys that differ from the base; those override the matching base keys, while everything else falls through to application.properties. The same applies to YAML with application-<profile>.yml. This layering keeps shared configuration in one place and isolates the per-environment differences.

What happens if no profile is active?

Spring uses the default profile. That means beans not annotated with any @Profile are created normally, and only the base application.properties (and any properties for a configured default profile) apply; profile-specific files like application-dev.properties are not loaded. It is good practice to make the no-profile, default behavior safe and conservative, and to require an explicit profile for environments that need special configuration, so nothing accidentally runs with the wrong settings.

Related lessons