Use @Profile to Stop That Madness
Situation
We’ve all been there: you’re building a feature that sends emails—maybe it’s a password reset, a newsletter, or a welcome message. You test it locally, everything looks good, and then… you forget to switch something before deploying. Suddenly, real users are receiving test emails with dummy links or placeholder content. Ouch.
Even worse? Maybe you’re in a test environment, and emails aren’t sent at all, because the SMTP server is missing, misconfigured, or the credentials expired. Now you’re debugging not your app, but infrastructure.
In environments like dev, test, and production, the behavior of email sending should be different, but the code should stay the same.
Task
The task is clear: prevent email logic from leaking into the wrong environment. Your dev/test environments should not send real emails, but your prod environment must send them reliably. And you don’t want fragile if (env.equals("prod"))
logic scattered across your codebase.
You want clean, maintainable, environment-specific email behavior with zero risk of mixing environments.
Action
Enter Spring’s @Profile
.
Using @Profile
, you can define multiple implementations of the same interface and let Spring automatically choose the right one depending on the active environment. Let’s look at an example:
Step 1: Define an Interface
public interface MailSender {
void send(String to, String message);
}
Step 2: Create Environment-Specific Implementations
@Component
@Profile("prod")
public class SmtpMailSender implements MailSender {
public void send(String to, String message) {
// Send email via SMTP
System.out.println("SMTP: Email sent to " + to);
}
}
@Component
@Profile("dev")
public class MockMailSender implements MailSender {
public void send(String to, String message) {
// Log email instead of sending
System.out.println("DEV: Mock email to " + to + ": " + message);
}
}
Now Spring will only register one of them based on your active profile.
Step 3: Inject and Use Without Worry
@Service
public class AccountService {
private final MailSender mailSender;
public AccountService(MailSender mailSender) {
this.mailSender = mailSender;
}
public void sendWelcomeEmail(String userEmail) {
mailSender.send(userEmail, "Welcome to our app!");
}
}
Step 4: Activate Profiles Easily
- In
application.properties
: propertiesCopiarEditarspring.profiles.active=dev
- Or via CLI:
java -Dspring.profiles.active=prod -jar app.jar
Result
Now you can:
- Safely test email logic in dev without ever risking real emails going out.
- See mock output in logs to verify formatting and triggers.
- Ensure real emails go out in production with zero changes to your business logic.
- Avoid environment conditionals and keep your code clean and maintainable.
Final Considerations: SOLID Principles in Action
This approach isn’t just a trick — it’s an application of solid software design principles:
- S – Single Responsibility: Each
MailSender
implementation has one job—either mock or real sending. - O – Open/Closed Principle: You can add new environments (
test
,qa
,staging
) without touching existing code. - L – Liskov Substitution: Both
MockMailSender
andSmtpMailSender
can be used interchangeably via theMailSender
interface. - I – Interface Segregation: The interface is simple, with just the method needed to send an email.
- D – Dependency Inversion:
AccountService
depends on an abstraction (MailSender
), not a concrete implementation.
By leveraging Spring’s @Profile
, you’re not just solving a practical problem — you’re applying architectural discipline that will scale with your app.
Leave a Reply
You must be logged in to post a comment.