# Spring Profiles

## About

**Spring Profiles** provide a mechanism to separate **environment-specific** bean definitions and configuration logic, so our application can adapt automatically to different runtime contexts (e.g., development, testing, staging, production).

Instead of manually commenting/uncommenting configuration code for each environment, we can annotate beans or configuration classes with `@Profile` so they are only **loaded** if the matching profile is **active**.

## Purpose

Spring Profiles exist to **isolate and control bean registration based on runtime context**, enabling one application codebase to serve multiple operational needs without invasive code changes.

In modern applications, environments differ in **infrastructure, performance requirements, security policies, and integration endpoints**. If we tried to support all environments with a single unfiltered configuration, we would face:

* **Complex conditional logic** scattered across the codebase
* **High risk of misconfiguration**, especially when moving from lower to higher environments
* **Hard-to-reproduce bugs** caused by unintended bean inclusion

Spring Profiles solve this by giving the container **selective visibility** into which beans should be considered at startup. The decision is made **before bean instantiation**, so irrelevant beans are never even created.

**Key reasons profiles exist:**

1. **Environment Specialization**
   * Dev might use an in-memory H2 database; prod might use a cloud-hosted Postgres.
   * Allows infrastructure changes without touching core logic.
2. **Behavior Swapping**
   * Swap a real payment gateway with a mock gateway in tests.
   * Replace complex external integrations with local stubs for faster local development.
3. **Deployment Flexibility**
   * The same artifact (JAR/WAR) can be deployed to multiple environments by simply changing an activation flag - no rebuild required.
4. **Risk Reduction**
   * Prevents accidental use of production-only beans in development/testing contexts.
   * Eliminates “wrong environment” incidents by physically excluding beans.
5. **Clean Separation of Concerns**
   * Configuration is moved **out of business logic** into declarative annotations or config files.
   * Developers no longer need to read conditional code to understand environment rules.
6. **Scalability in Large Teams**
   * Teams working on different modules can define profile-specific beans without interfering with each other’s configurations.
   * Encourages modularity in infrastructure definitions.
7. **Runtime Adaptability**
   * Supports complex setups like *multi-cloud deployments*, *hybrid on-prem/cloud*, or *feature-flag-driven releases*, where bean composition changes based on active profile combinations.

In short, the purpose of Spring Profiles is **not just “different config for different environments”**, but to **build flexible, safe, and maintainable application configurations** that scale across multiple deployment contexts - all while keeping the main codebase clean and environment-agnostic.

## How It Works ?

Spring Profiles operate at the **bean definition registration stage** - *before* any beans are instantiated, but *after* all configuration sources have been loaded into the `Environment`.

Here’s the step-by-step breakdown:

### **1. Profile Resolution Phase**

When the Spring application starts, the container determines which profiles are **active** and which are **default**:

1. **Sources for profile activation** (checked in priority order):
   * Command-line arguments (`--spring.profiles.active=...`)
   * JVM system properties (`-Dspring.profiles.active=...`)
   * Environment variables (`SPRING_PROFILES_ACTIVE`)
   * Application properties/YAML files
   * Programmatic calls (`ConfigurableEnvironment#setActiveProfiles`)
2. If **no active profiles** are explicitly set, the container:
   * Falls back to `spring.profiles.default`
   * If `spring.profiles.default` is also not set, uses `"default"` as the implicit profile.

### **2. Bean Definition Phase**

Before bean instantiation:

* Spring **parses** all `@Configuration` classes, `@Component` classes, and imported configurations.
* Each bean definition is checked for **profile metadata**:
  * Beans with **no `@Profile`** → eligible for all profiles.
  * Beans with `@Profile` → only eligible if **at least one** profile matches the currently active set.

{% hint style="success" %}
Profile checks happen **before** any dependency resolution or `@Autowired` injection.
{% endhint %}

### **3. Matching Logic**

Spring uses **set intersection logic**:

* Let **A** = Set of active profiles (from `Environment`)
* Let **B** = Set of profiles declared on the bean via `@Profile`
* If **A ∩ B** is **non-empty** → Bean is registered.
* If empty → Bean definition is discarded.

### **4. Profile Expressions**

Spring 4+ allows logical operators in `@Profile`:

* `@Profile({"dev", "test"})` → OR condition
* `@Profile("dev & mysql")` → AND condition
* `@Profile("!prod")` → NOT condition

Expression parsing is handled by `ProfilesParser`, which creates a **predicate** evaluated against the active profile set.

### **5. Bean Registration Outcome**

After profile filtering:

* Only **matching beans** remain in the `BeanDefinitionRegistry`.
* Dependency injection operates **only** on the filtered set.
* Beans excluded due to profile mismatch:
  * Are never instantiated
  * Do not participate in dependency resolution
  * Do not trigger `@PostConstruct` or `InitializingBean` hooks

### **6. Special Cases**

* **Overlapping profiles**: Multiple beans for the same type may be active if profiles overlap. Use `@Primary` or `@Qualifier` to resolve ambiguity.
* **Profile-specific configuration classes**: If a `@Configuration` class is annotated with `@Profile`, **all beans inside** are subject to the same profile restriction.
* **Default profile override**: If `spring.profiles.active` is set, the default profile is ignored.

## **Naming Convention**

The file naming must follow:

```
application-<profile>.yaml
application-<profile>.properties
```

Where `<profile>` exactly matches the profile name declared in:

* `spring.profiles.active`
* `SPRING_PROFILES_ACTIVE` environment variable
* CLI argument (`--spring.profiles.active=...`)
* Programmatic activation.

## Declaring Profiles

In Spring, **declaring a profile** means associating a bean definition (or configuration class) with one or more **environment identifiers** so that the bean is **only loaded** when those profiles are **active**.\
This declaration is a **filtering mechanism** during the *bean registration phase*, not during bean execution.

When the container is starting up, it evaluates all bean definitions against the **currently active profile set** before even instantiating them. If the bean’s `@Profile` condition is **not satisfied**, the bean is **never registered** in the `ApplicationContext`. This is important because:

* The bean's lifecycle methods (`@PostConstruct`, `@PreDestroy`) are never called.
* Other beans cannot inject it because it doesn’t exist in the context.
* This is more efficient than loading all beans and disabling them at runtime.

#### **1. Applying `@Profile` on Bean Classes**

When placed on a class that is a **Spring bean** (`@Component`, `@Service`, `@Repository`, `@Controller`), `@Profile` tells Spring to register that bean only when the matching profile is active.

```java
@Service
@Profile("dev")
public class DevPaymentService implements PaymentService {
    public void pay() { System.out.println("Processing payment in DEV mode"); }
}
```

* **Effect**: Only available when `spring.profiles.active=dev` (or when `dev` is part of a multi-profile setup).
* If `dev` is not active, the container will **not even know** this bean exists.

#### **2. Applying `@Profile` on `@Bean` Methods**

Profiles can also be declared **at the method level** inside a `@Configuration` class.\
This allows us to **conditionally define beans** without splitting the configuration into multiple classes.

```java
@Configuration
public class DataSourceConfig {

    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        return new HikariDataSource(); // connects to production DB
    }

    @Bean
    @Profile("test")
    public DataSource testDataSource() {
        return new EmbeddedDatabaseBuilder().setType(H2).build(); // in-memory DB
    }
}
```

* Here, only **one** `DataSource` bean is created depending on the active profile.
* This prevents duplicate beans and ambiguity errors.

#### **3. Applying `@Profile` on Configuration Classes**

When applied at the **class level** to a `@Configuration` class, **all bean methods inside** inherit that profile condition.

```java
@Configuration
@Profile("staging")
public class StagingConfiguration {

    @Bean
    public EmailService stagingEmailService() {
        return new SmtpEmailService("staging-mail.company.com");
    }

    @Bean
    public PaymentService stagingPaymentService() {
        return new PaymentGateway("https://staging-payments.company.com");
    }
}
```

* This approach keeps environment-specific configurations **grouped together**.
* Useful when multiple beans change together between environments.

#### **4. Multi-Profile Declarations**

We can assign **multiple profiles** to the same bean or configuration class:

```java
@Component
@Profile({"qa", "staging"})
public class QAAndStagingLogger implements Logger { ... }
```

* The bean will be active if **any** listed profile matches.
* This is an **OR** condition by default.

#### **5. Profile Expressions for Complex Conditions**

Spring 4+ supports **logical profile expressions**:

* **AND**: `"dev & mysql"` → both must be active.
* **OR**: `"dev | qa"` → either one is active.
* **NOT**: `"!prod"` → active in all profiles except prod.

Example:

```java
@Component
@Profile("dev & !cloud")
public class LocalDevFileStorage implements FileStorage { ... }
```

* Bean loads only in **local dev environments**, not in cloud deployments.

#### **6. Default Profile Binding**

If we don’t explicitly set a profile, beans without a `@Profile` annotation are considered **profile-neutral** - they load in **all environments**.\
But we can **explicitly** bind beans to the `default` profile:

```java
@Component
@Profile("default")
public class DefaultMetricsService implements MetricsService { ... }
```

* This is useful for having a **fallback configuration** when no profiles are active.

## Default Profile

In Spring, the **default profile** is a **fallback** profile that is applied **when no explicit profile is active**.

* It is internally represented as `"default"`.
* Beans annotated with `@Profile("default")` are **only** loaded if:
  * No profile is set via `spring.profiles.active` (properties, YAML, env vars, CLI, etc.).
  * No profile is activated programmatically.
* Beans **without any `@Profile` annotation** are *not tied* to any profile and are always loaded regardless of active profile(s).

{% hint style="info" %}
**No `@Profile`** → bean is *always included*.

`@Profile("default")` → bean is included *only if nothing else is active*.
{% endhint %}

#### **Why Have a Default Profile ?**

* **Local development fallback**: When running the app without specifying a profile, it still starts with sensible defaults.
* **Baseline configuration**: Shared settings that don’t belong to any specific environment but are needed for the app to run.
* **Avoid startup failures**: Ensures the application has at least one set of beans to load.

#### **How It Works Internally ?**

* At startup, Spring checks for active profiles:
  * If **none** are set → `"default"` profile is automatically active.
  * If **one or more** profiles are active → `"default"` profile is **not** added unless explicitly included in `spring.profiles.active`.
* The `"default"` profile is just a **label** like any other; it’s not special beyond the auto-activation rule.

#### **application.yaml vs application-\<profile>.yaml**

Spring Boot automatically loads configuration files based on active profiles:

Loading Order:

1. `application.yaml` (or `.properties`) → Always loaded first.
2. `application-<profile>.yaml` → Loaded only if `<profile>` is active.
3. Profile-specific files override values from the base file.

#### **A. Example – No Active Profile**

**Files:**

```yaml
# application.yaml
spring:
  datasource:
    url: jdbc:h2:mem:default
```

```yaml
# application-dev.yaml
spring:
  datasource:
    url: jdbc:mysql://localhost/devdb
```

**Run without `spring.profiles.active`**:

* Active profile → `"default"`
* Loaded configs:
  * application.yaml (H2 in-memory DB)
  * application-dev.yaml is **ignored**.

#### **B. Example – Active Profile is dev**

**Run with**:

```bash
java -jar app.jar --spring.profiles.active=dev
```

* Active profile → `"dev"`
* Loaded configs:
  * application.yaml (base config)
  * application-dev.yaml (overrides datasource URL to MySQL)
* `"default"` profile beans are **ignored**.

#### **C. Example – Multiple Profiles**

```bash
java -jar app.jar --spring.profiles.active=dev,aws
```

* application.yaml (base config)
* application-dev.yaml (dev-specific overrides)
* application-aws.yaml (AWS-specific overrides)
* All beans with `@Profile("dev")` or `@Profile("aws")` are eligible.

## Activating Profiles

#### **A. Properties/YAML**

```properties
spring.profiles.active=dev
spring.profiles.default=default
```

```yaml
spring:
  profiles:
    active: dev
    default: default
```

#### **B. Command-Line**

```bash
java -jar app.jar --spring.profiles.active=prod
```

#### **C. JVM System Property**

```bash
java -Dspring.profiles.active=staging -jar app.jar
```

#### **D. Environment Variable**

```bash
export SPRING_PROFILES_ACTIVE=test
```

#### **E. Programmatically**

```java
SpringApplication app = new SpringApplication(MyApp.class);
app.setAdditionalProfiles("dev");
app.run(args);
```

#### **F. Multi-Profile Beans**

A single bean can match **multiple profiles**:

```java
@Profile({"qa", "staging"})
@Component
public class StagingPaymentService implements PaymentService { ... }
```

If **either** profile is active, the bean will be loaded.

#### **G. Testing with Profiles**

In tests, we can activate profiles with:

```java
@SpringBootTest
@ActiveProfiles("test")
public class PaymentServiceTest { ... }
```

This ensures test beans replace production beans automatically.

#### **H. Combining Profiles with Conditional Beans**

`@Profile` can be combined with:

* `@ConditionalOnProperty`
* `@ConditionalOnClass`
* `@ConditionalOnMissingBean`\
  …for even more precise bean activation control.

Example:

```java
@Bean
@Profile("cloud")
@ConditionalOnProperty(name = "storage.type", havingValue = "s3")
public FileStorage s3FileStorage() { ... }
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.pranaypourkar.co.in/the-programmers-guide/spring/core-concepts/profiles.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
