Skip to content

Spring AOP and Conditional Bean Creation Guide

Environment Configuration Without Profiles

Section titled “Environment Configuration Without Profiles”

Alternatives to @Profile for enabling/disabling beans:

@Bean
@ConditionalOnProperty(name="feature.payment.enabled", havingValue="true")
public PaymentService paymentService() {
return new PaymentService();
}
  • Feature flags
  • Environment variables
  • Spring Cloud Config
Feature @Profile @ConditionalOnXXX
Purpose Environment-based bean creation Conditional bean creation
Based on Active profile Property, class, bean, resource, expression
Flexibility Limited Highly flexible
Typical Use dev/test/prod Feature toggles and auto-configuration

Examples:

@Profile("dev")
@ConditionalOnProperty

AOP separates cross-cutting concerns from business logic.

Common use cases:

  • Logging
  • Security
  • Transactions
  • Auditing
  • Performance monitoring

A point during execution, such as a method execution.

An expression selecting join points.

@Pointcut("execution(* com.service.*.*(..))")
Advice Execution Time
@Before Before method execution
@After After method execution
@AfterReturning After successful completion
@AfterThrowing When an exception occurs
@Around Before and after execution
flowchart LR
A[Caller] --> B[AOP Proxy]
B --> C["@Before"]
C --> D["@Around Before"]
D --> E[Target Method]
E --> F["@AfterReturning / @AfterThrowing"]
F --> G["@After"]
G --> H[Caller]

Advice methods live inside a class marked @Aspect (and @Component, so Spring registers it as a bean):

@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.service.*.*(..))")
public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
log.info("Execution time: {}", end - start);
return result;
}
}

Multiple aspects can target the same service independently:

flowchart LR
Client --> Service
LoggingAspect --> Service
SecurityAspect --> Service
TransactionAspect --> Service
@Around("@annotation(Loggable)")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Request: {}", Arrays.toString(joinPoint.getArgs()));
Object response = joinPoint.proceed();
log.info("Response: {}", response);
return response;
}

This pattern is commonly used for request/response logging, audit logging, API tracing, and performance monitoring — without duplicating that logic inside every service method.

  • Prefer @ConditionalOnXXX over @Profile when the decision is a feature toggle rather than a dev/test/prod environment split — it’s driven by properties, classpath, or other beans instead of a fixed profile name.
  • AOP terminology: a Join Point is a point in execution (e.g. a method call), a Pointcut is an expression selecting which join points to intercept, and Advice is the code that runs at those join points.
  • @Around is the most powerful advice type — it can run code both before and after the target method, and controls whether the method executes at all via joinPoint.proceed().
  • Use AOP for cross-cutting concerns (logging, performance timing, auditing) instead of duplicating that code inside every method it applies to — this is also how Spring implements @Transactional and @Cacheable internally.