In the dynamic world of Spring applications, managing which beans are active at runtime, how they are named, and how to precisely inject them is crucial. Spring offers a suite of conditional annotations, naming conventions, and injection mechanisms to achieve this flexibility. Let’s delve into them:
Naming Beans
-
Default Naming: By default, Spring uses the following strategies to name beans:
@Component
,@Service
,@Repository
,@Controller
: The class name with the first letter lowercase (e.g.,myService
forMyService
).@Bean
: The method name (e.g.,dataSource
for@Bean public DataSource dataSource() { ... }
).
-
Custom Naming:
@Component
,@Service
, etc.: Provide a value within the annotation (e.g.,@Service("myCustomService")
).@Bean
: Use thename
attribute (e.g.,@Bean(name = "myCustomBean")
).
Specifying Beans by Name in Injection
When you have multiple beans of the same type, you need a way to tell Spring which one to inject. Here are the primary methods:
-
@Qualifier
- The most common and recommended approach.
- Use it with
@Autowired
or@Inject
. - Example:
@Autowired @Qualifier("myCustomBean") private MyBean myBean;
-
@Resource
- Offers similar functionality to
@Qualifier
but is part of the Java EE standard, not Spring-specific. - Can also match by type if no name is provided.
- Example:
@Resource(name = "myCustomBean") private MyBean myBean;
- Offers similar functionality to
Conditional Bean Creation
Conditional Bean Creation
1. @ConditionalOnProperty
- Use Case: Controls bean creation based on specific property values or the existence of a property.
- Example:
@Configuration
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
@Primary // This bean will be the primary choice if multiple beans of the same type exist
public class FeatureBean { ... }
- Explanation:
FeatureBean
is only created if the property “feature.enabled” exists and has the value “true”. If multiple beans of the same type are created,FeatureBean
will be the primary choice due to the@Primary
annotation.
2. @ConditionalOnClass
and @ConditionalOnMissingClass
- Use Case: Ideal for handling optional dependencies. A bean is created only if a specific class is present (or missing) in the classpath.
- Example:
@Configuration
@ConditionalOnClass(name = "com.example.OptionalDependency")
@Primary
public class DependentBean { ... }
- Explanation:
DependentBean
is only created if the class “com.example.OptionalDependency” is available. If multiple beans of the same type are created,DependentBean
will be the primary choice.
3. @ConditionalOnBean
and @ConditionalOnMissingBean
- Use Case: Enables you to create beans based on the presence (or absence) of other beans within the Spring context.
- Example:
@Configuration
@ConditionalOnBean(DataSource.class)
@Primary
public class DatabaseBean { ... }
- Explanation:
DatabaseBean
is only created if a bean of typeDataSource
already exists in the context. If multiple beans of the same type are created,DatabaseBean
will be the primary choice.
4. @Profile
- Use Case: While not strictly a conditional annotation,
@Profile
allows you to activate beans based on active Spring profiles. - Example:
@Configuration
@Profile("development")
@Primary
public class DevBean { ... }
- Explanation:
DevBean
is only created when the “development” profile is active. If multiple beans of the same type are created,DevBean
will be the primary choice.
The Role of @Primary
In Spring, it’s possible to have multiple beans of the same type within your application context. This can occur intentionally (e.g., providing different implementations for different scenarios) or unintentionally (e.g., third-party libraries contributing beans). When autowiring or injecting beans by type, Spring needs a way to determine which bean to use if there are multiple candidates.
This is where @Primary
comes in. When you annotate a bean with @Primary
, you’re essentially marking it as the preferred choice if there are multiple beans of the same type available. Spring will prioritize injecting the @Primary
bean unless you explicitly specify a different bean using @Qualifier
or @Resource
.
Example:
@Configuration
@Primary
public class DefaultEmailService implements EmailService { ... }
@Configuration
public class BackupEmailService implements EmailService { ... }
@Service
public class MyService {
@Autowired
private EmailService emailService; // This will inject DefaultEmailService
// ...
}
In this example, both DefaultEmailService
and BackupEmailService
implement the EmailService
interface. Since DefaultEmailService
is marked with @Primary
, it will be the one injected into MyService
by default.
Important Considerations:
@Primary
only influences bean selection when there are multiple beans of the same type. It doesn’t affect the creation or existence of the bean itself.- You can combine
@Primary
with conditional annotations to create scenarios where a bean is only primary under certain conditions. - If you have multiple
@Primary
beans of the same type, Spring will throw an exception during startup, indicating an ambiguous situation. - Combining Annotations: Combine multiple conditional annotations for fine-grained control.
- Custom Conditions: Create custom conditions by implementing the
Condition
interface.
Spring’s conditional annotations, naming conventions, injection mechanisms (@Qualifier
, @Resource
), and the @Primary
annotation provide a powerful toolkit for creating dynamic and adaptable applications. By mastering these tools, you gain precise control over your bean configuration, injection, and selection, allowing you to tailor your application to various environments and runtime conditions.
Discover more from GhostProgrammer - Jeff Miller
Subscribe to get the latest posts sent to your email.