Spring Boot provides a variety of conditional assembly technologies, allowing developers to dynamically configure applications according to different conditions, greatly improving application flexibility. This article will introduce four commonly used conditional assembly technologies in Spring Boot.
1. @Conditional annotation and derived annotation
1. Basic Principles
@Conditional
Annotations are the core conditional assembly mechanism introduced by Spring 4. It allows developers to decide whether to create a bean or enable a configuration based on specific conditions.
@Conditional
The basic working principle is: when Spring container processing is@Conditional
When defining the bean of the annotation, the specified condition will be evaluated first. Only when the condition is met will the bean or application configuration be created.
2. @Conditional Basic Usage
use@Conditional
When annotating, you need to specify an implementationCondition
Conditional classes for interfaces:
public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
Custom condition class example:
public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = (); String os = (""); return os != null && ().contains("linux"); } } public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = (); String os = (""); return os != null && ().contains("windows"); } }
Then, use these conditional classes to determine the creation of the bean:
@Configuration public class OperatingSystemConfig { @Bean @Conditional() public CommandService linuxCommandService() { return new LinuxCommandService(); } @Bean @Conditional() public CommandService windowsCommandService() { return new WindowsCommandService(); } }
The above configuration will create different operating system types according to the operating environment.CommandService
accomplish.
3. Commonly used derivative annotations
Spring Boot provides a series of@Conditional
The derived annotation simplifies the configuration of common condition judgments:
@ConditionalOnClass/@ConditionalOnMissingClass
Determine the configuration based on whether there is a specific class in the classpath:
@Configuration public class JpaConfig { @Bean @ConditionalOnClass(name = "") public LocalContainerEntityManagerFactoryBean entityManagerFactory() { // This bean is created only if there is a JPA-related class in the classpath return new LocalContainerEntityManagerFactoryBean(); } @Bean @ConditionalOnMissingClass("") public JdbcTemplate jdbcTemplate() { // When JPA-related classes do not exist in the classpath, use JdbcTemplate return new JdbcTemplate(); } }
@ConditionalOnBean/@ConditionalOnMissingBean
Determine the configuration based on whether there is a specific bean in the container:
@Configuration public class DataSourceConfig { @Bean @ConditionalOnMissingBean public DataSource defaultDataSource() { // Create a default data source when there is no Bean of DataSource in the container return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .build(); } @Bean @ConditionalOnBean(name = "customDataSourceProperties") public DataSource customDataSource(CustomDataSourceProperties properties) { // Create a custom data source when there is a bean named customDataSourceProperties HikariDataSource dataSource = new HikariDataSource(); (()); (()); (()); return dataSource; } }
@ConditionalOnProperty
Determine the configuration based on the value of the configuration attribute:
@Configuration public class CacheConfig { @Bean @ConditionalOnProperty(name = "", havingValue = "redis") public CacheManager redisCacheManager() { // When the property value is redis, configure the Redis cache manager return new RedisCacheManager(); } @Bean @ConditionalOnProperty(name = "", havingValue = "ehcache") public CacheManager ehCacheManager() { // When the property value is ehcache, configure the EhCache cache manager return new EhCacheCacheManager(); } @Bean @ConditionalOnProperty(name = "", havingValue = "false", matchIfMissing = true) public CacheManager noOpCacheManager() { // Use empty operation cache manager when false or not set return new NoOpCacheManager(); } }
@ConditionalOnExpression
Determine the configuration based on the results of the SpEL expression:
@Configuration public class SecurityConfig { @Bean @ConditionalOnExpression("${:true} and ${:basic} == 'oauth2'") public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http) throws Exception { // Effective when true and oauth2 return http .oauth2Login() .and() .build(); } @Bean @ConditionalOnExpression("${:true} and ${:basic} == 'basic'") public SecurityFilterChain basicSecurityFilterChain(HttpSecurity http) throws Exception { // Effective when true and basic return http .httpBasic() .and() .build(); } }
@ConditionalOnWebApplication/@ConditionalOnNotWebApplication
Determine the configuration based on whether the application is a web application:
@Configuration public class ServerConfig { @Bean @ConditionalOnWebApplication public ServletWebServerFactory servletWebServerFactory() { // This bean is created only in a web application return new TomcatServletWebServerFactory(); } @Bean @ConditionalOnNotWebApplication public ApplicationRunner consoleRunner() { // This bean is created only in non-web applications return args -> ("Running as a console application"); } }
4. Practical example: Build applications that adapt to different cache environments
Here is a practical example to show how to use it@Conditional
Series annotations build an application that can adapt to different cache environments:
@Configuration public class FlexibleCacheConfiguration { @Bean @ConditionalOnClass(name = "") @ConditionalOnProperty(name = "", havingValue = "redis") @ConditionalOnMissingBean() public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { builder = (redisConnectionFactory); return (); } @Bean @ConditionalOnClass(name = ".") @ConditionalOnProperty(name = "", havingValue = "ehcache") @ConditionalOnMissingBean() public CacheManager ehCacheCacheManager() { return new JCacheCacheManager(getJCacheCacheManager()); } @Bean @ConditionalOnProperty( name = "", havingValue = "simple", matchIfMissing = true ) @ConditionalOnMissingBean() public CacheManager simpleCacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); (( new ConcurrentMapCache("users"), new ConcurrentMapCache("transactions"), new ConcurrentMapCache("products") )); return cacheManager; } @Bean @ConditionalOnProperty(name = "", havingValue = "false") @ConditionalOnMissingBean() public CacheManager noOpCacheManager() { return new NoOpCacheManager(); } private getJCacheCacheManager() { // The logic for creating JCache CacheManager... return null; // The actual code needs to return the real CacheManager } }
In the above configuration:
- If there is a Redis-related class in the classpath and it is configured
=redis
, then use Redis cache - If there is an EhCache-related class in the classpath and it is configured
=ehcache
, then use EhCache - If configured
=simple
or if type is not specified, use a simple memory cache - If configured
=false
, then use NoOpCacheManager that does not perform any cache operations
5. Pros and cons analysis
advantage:
- Flexible and powerful, able to adapt to almost all conditions to judge scenarios
- Seamless integration with Spring ecosystem
- Derived annotations simplify configuration of common scenarios
- Conditional judgment logic is separated from business logic to keep the code clear
shortcoming:
- Complex conditions can make configuration difficult to understand and debug
- The order of conditional assembly may affect the final bean definition
2. Profile condition configuration
1. Basic Principles
Profile is another conditional assembly mechanism provided by Spring, which is mainly used to manage the creation of beans by environment (such as development, testing, production). and@Conditional
Compared with Profile, it focuses more on environment distinction and is simpler to configure.
2. Usage of @Profile annotation
use@Profile
Annotation tags beans or configuration classes, specifying which profiles they are created only when they are activated:
@Configuration public class DataSourceConfig { @Bean @Profile("development") public DataSource developmentDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .build(); } @Bean @Profile("production") public DataSource productionDataSource() { HikariDataSource dataSource = new HikariDataSource(); ("jdbc:mysql://localhost:3306/proddb"); ("produser"); ("prodpass"); return dataSource; } }
It can also be applied at the configuration class level@Profile
Annotation, controls the activation of the entire configuration class:
@Configuration @Profile("development") public class DevelopmentConfig { // The unique Bean definition of the development environment...} @Configuration @Profile("production") public class ProductionConfig { // The unique definition of beans in the production environment...}
3. How to activate Profile
There are several ways to activate a specified Profile:
Through configuration file
existor
middle:
# =development
or
# spring: profiles: active: development
Pass command line parameters
java -jar --=production
Through environment variables
export SPRING_PROFILES_ACTIVE=production java -jar
Activate via code
@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(); ("production"); (args); } }
4. Profile combination and negation
Spring Boot 2.4 and above provide more flexible Profile expressions:
Using Profile Group
# =proddb,prodmq =devdb,devmq
The above configuration defines two Profile groups: when "production" is activated, "proddb" and "prodmq" are activated at the same time; when "development" is activated, "devdb" and "devmq" are activated at the same time.
Use negative expressions
@Bean @Profile("!development") public MonitoringService productionMonitoringService() { return new DetailedMonitoringService(); }
The above configuration indicates that all Profiles except "development" will create this bean.
5. Practical example: Profile-based message queue configuration
The following is a practical example to show how to use Profile to configure message queue connections in different environments:
@Configuration public class MessagingConfig { @Bean @Profile("local") public ConnectionFactory localConnectionFactory() { // Local development uses embedded ActiveMQ return new ActiveMQConnectionFactory("vm://localhost?=false"); } @Bean @Profile("dev") public ConnectionFactory devConnectionFactory() { // The development environment uses RabbitMQ on the development server CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); (""); ("dev_user"); ("dev_pass"); return connectionFactory; } @Bean @Profile("prod") public ConnectionFactory prodConnectionFactory() { // Use production-grade RabbitMQ clusters in the production environment CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); (","); ("prod_user"); ("prod_pass"); // Additional configuration is added in the production environment (true); (true); return connectionFactory; } @Bean public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { // General RabbitTemplate configuration, using the ConnectionFactory corresponding to the current Profile return new RabbitTemplate(connectionFactory); } }
Combined with configuration files for specific environments:
# spring: rabbitmq: listener: simple: concurrency: 1 max-concurrency: 5 # spring: rabbitmq: listener: simple: concurrency: 5 max-concurrency: 10 # spring: rabbitmq: listener: simple: concurrency: 10 max-concurrency: 50 retry: enabled: true initial-interval: 5000 max-attempts: 3
6. Pros and cons analysis
advantage:
- Simple and intuitive use, designed specifically for environmental distinction
- Perfect integration with Spring Boot configuration system
- Support combination and negative expressions to enhance expression capabilities
- Profiles can be switched in various ways to adapt to different deployment scenarios
shortcoming:
- Limited expression ability, not as good as
@Conditional
Flexible annotation - Mainly based on a predefined naming environment, it has weak ability to handle dynamic conditions
3. Automatic configuration conditions
1. Basic Principles
Automatic configuration is one of the core features of Spring Boot. It allows the framework to automatically configure applications based on conditions such as classpath, existing beans and configuration properties. Automatic configuration of conditions is the basis for implementing this function. It realizes complex condition judgment logic by combining multiple condition annotations.
2. Commonly used automatic configuration condition combination
In Spring Boot's automatic configuration class, you can often see a combination of multiple conditional annotations:
@Configuration @ConditionalOnClass() @ConditionalOnMissingBean() @ConditionalOnProperty(prefix = "", name = "enabled", matchIfMissing = true) public class DataSourceAutoConfiguration { // Automatic configuration of data source...}
The above configuration indicates:
- Only if the classpath exists
DataSource
kind - And there is no
DataSource
Type of bean - and
When the property does not exist or is true
- This automatic configuration class will be enabled
3. Customize automatic configuration classes
Developers can create their own automatic configuration classes and use conditional annotations to control their activation conditions:
@Configuration @ConditionalOnClass() @ConditionalOnMissingBean() @ConditionalOnProperty(prefix = "mycache", name = "type", havingValue = "redis") @EnableConfigurationProperties() public class RedisCacheAutoConfiguration { @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, MyCacheProperties properties) { builder = (redisConnectionFactory); if (() > 0) { (() .entryTtl((()))); } return (); } }
Configure property class:
@ConfigurationProperties(prefix = "mycache") public class MyCacheProperties { private String type; private int expireTime = 3600; // getters and setters }
4. Enable automatic configuration
To enable a custom autoconfiguration class, you need to createMETA-INF/
document:
=\
Or in Spring Boot 2.7 and above, you can use itMETA-INF/spring/
document:
5. Automatic configuration sequence control
In complex systems, it may be necessary to control the loading order of the automatic configuration class, which can be done by@AutoConfigureBefore
、@AutoConfigureAfter
and@AutoConfigureOrder
Annotation implementation:
@Configuration @ConditionalOnClass() @AutoConfigureAfter() public class JdbcTemplateAutoConfiguration { // JDBC template is automatically configured to ensure that it is after the data source is configured} @Configuration @ConditionalOnClass() @AutoConfigureBefore() public class SecurityAutoConfiguration { // The security configuration should be before the Web MVC configuration} @Configuration @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) public class EarlyInitAutoConfiguration { // The configuration that needs to be initialized first}
6. Practical example: Automatic configuration of custom monitoring system
Here is a practical example to show how to create a pluggable application monitoring component using automatic configuration conditions:
//Configure property class@ConfigurationProperties(prefix = "") public class MonitoringProperties { private boolean enabled = true; private String type = "jmx"; private int sampleRate = 10; private boolean logMetrics = false; // getters and setters } // Automatic configuration of JMX monitoring@Configuration @ConditionalOnProperty(prefix = "", name = "enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(prefix = "", name = "type", havingValue = "jmx", matchIfMissing = true) @ConditionalOnClass(name = "") @EnableConfigurationProperties() public class JmxMonitoringAutoConfiguration { @Bean @ConditionalOnMissingBean public MetricsCollector metricsCollector(MonitoringProperties properties) { JmxMetricsCollector collector = new JmxMetricsCollector(); (()); return collector; } @Bean @ConditionalOnMissingBean public MetricsExporter metricsExporter(MonitoringProperties properties) { JmxMetricsExporter exporter = new JmxMetricsExporter(); (()); return exporter; } } // Prometheus monitoring automatic configuration@Configuration @ConditionalOnProperty(prefix = "", name = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "", name = "type", havingValue = "prometheus") @ConditionalOnClass(name = "") @EnableConfigurationProperties() public class PrometheusMonitoringAutoConfiguration { @Bean @ConditionalOnMissingBean public MetricsCollector metricsCollector(MonitoringProperties properties) { PrometheusMetricsCollector collector = new PrometheusMetricsCollector(); (()); return collector; } @Bean @ConditionalOnMissingBean public MetricsExporter metricsExporter(MonitoringProperties properties) { PrometheusMetricsExporter exporter = new PrometheusMetricsExporter(); (()); return exporter; } @Bean public CollectorRegistry collectorRegistry() { return new CollectorRegistry(true); } @Bean public HttpHandler prometheusEndpoint(CollectorRegistry registry) { return new PrometheusHttpHandler(registry); } } // Automatic configuration of log monitoring@Configuration @ConditionalOnProperty(prefix = "", name = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "", name = "type", havingValue = "log") @EnableConfigurationProperties() public class LogMonitoringAutoConfiguration { @Bean @ConditionalOnMissingBean public MetricsCollector metricsCollector(MonitoringProperties properties) { LogMetricsCollector collector = new LogMetricsCollector(); (()); return collector; } @Bean @ConditionalOnMissingBean public MetricsExporter metricsExporter() { return new LogMetricsExporter(); } }
META-INF/file:
=\ ,\ ,\
Example of usage:
# Use JMX to monitor (default)app: monitoring: enabled: true type: jmx sample-rate: 5 log-metrics: true # Or use Prometheus to monitorapp: monitoring: enabled: true type: prometheus sample-rate: 10 # Or use log monitoringapp: monitoring: enabled: true type: log sample-rate: 30 # or disable monitoring completelyapp: monitoring: enabled: false
7. Pros and cons analysis
advantage:
- Implement the true "convention over configuration" principle
- It can create pluggable components, greatly improving code reusability
- Seamless integration with the Spring Boot ecosystem
shortcoming:
- The learning curve is steep and requires understanding the combination of multiple conditional annotations.
- Too many automatic configuration classes may increase application startup time
- Debugging is difficult, and troubleshooting problems requires in-depth understanding of Spring Boot startup mechanism
8. Summary
Conditional assembly technology | Core features | Main application scenarios | Complexity |
---|---|---|---|
@Conditional and derivative annotations | The most flexible, supports custom conditions | Scenarios that require complex conditions to judge | middle |
Profile conditional configuration | Focus on environmental distinction | Multi-environment deployment, environment-specific configuration | Low |
Automatic configuration conditions | Combining multiple conditions to achieve automatic configuration | Plugable components, framework development | high |
By rationally utilizing the conditional assembly technology provided by Spring Boot, developers can build flexible and configurable applications to meet the needs of different environments and business scenarios.
This is the end of this article about the detailed explanation of four commonly used conditional assembly technologies in SpringBoot. For more relevant SpringBoot conditional assembly technology content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!