十一、@ConditionalOnWebApplication
组合 @Conditional 注解,当前项目类型是 WEB 项目才开启配置。
当前项目有以下 3 种类型。
java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
Type type() default ConditionalOnWebApplication.Type.ANY;
public static enum Type {
ANY,
SERVLET,
REACTIVE;
private Type() {
}
}
}
11.1 作用
该注解的作用是:只有当 Spring 应用上下文是一个 Web 应用上下文时,被注解的类或方法才会被注册为 Bean 或生效。
这在自动配置(Auto-Configuration)中非常常见,用于区分 Web 应用和非 Web 应用(如批处理、命令行工具等)。
11.2 使用方式
11.2.1 注解在类上(通常用于 @Configuration 类)
java
@Configuration
@ConditionalOnWebApplication
public class WebConfig {
// 只有在 Web 应用中才会加载这个配置类
}
11.2.2 注解在 @Bean 方法上
java
@Bean
@ConditionalOnWebApplication
public MyWebBean myWebBean() {
return new MyWebBean();
}
11.3 可选类型(type 属性)
从 Spring Boot 2.0 开始,@ConditionalOnWebApplication 支持通过 type 属性进一步指定 Web 应用的类型:
java
@ConditionalOnWebApplication(type = Type.SERVLET)
// 或
@ConditionalOnWebApplication(type = Type.REACTIVE)
其中 Type 枚举包括:
Type.SERVLET:基于 Servlet 的 Web 应用(如使用 Tomcat、Jetty)Type.REACTIVE:响应式 Web 应用(如使用 WebFlux + Netty)Type.ANY(默认值):只要是 Web 应用(Servlet 或 Reactive 都算)
注意:如果不指定
type,默认是Type.ANY。
11.4 判断依据
Spring Boot 判断是否为 Web 应用的标准是:
- 是否存在
WebApplicationContext(即是否使用了 Web 相关的 ApplicationContext 实现) - 对于 Servlet 应用:是否存在
DispatcherServlet、ServletContext等 - 对于 Reactive 应用:是否存在
ReactiveWebApplicationContext
11.5 典型应用场景
- 自动配置 Web 相关的过滤器、拦截器、控制器等
- 避免在非 Web 应用(如 Spring Boot CLI 工具、批处理任务)中加载不必要的 Web 组件
- 区分 Servlet 和 Reactive 环境,提供不同的实现
11.6 示例:区分 Servlet 与 Reactive
java
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
public class ServletWebConfig {
// 仅在 Servlet 环境下生效
}
@Configuration
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class ReactiveWebConfig {
// 仅在 Reactive 环境下生效
}
11.7 总结
@ConditionalOnWebApplication 是 Spring Boot 条件化配置的重要工具之一,能帮助开发者根据应用类型动态启用或禁用某些配置,从而提升模块的通用性和灵活性。
十二、@ConditionalOnNotWebApplication
组合 @Conditional 注解,和 @ConditionalOnWebApplication 注解相反,当前项目类型不是 WEB 项目才开启配置。
12.1 作用
该注解用于仅在当前应用不是一个 Web 应用时,才加载被注解的配置类、Bean 或组件。
换句话说:
✅ 只有当应用是"非 Web 应用"(如命令行工具、批处理任务、后台服务等)时,该配置才会生效。
12.2 使用方式
12.2.1 注解在配置类上
java
@Configuration
@ConditionalOnNotWebApplication
public class BatchConfig {
// 仅在非 Web 应用中加载此配置
}
12.2.2 注解在 @Bean 方法上
java
@Bean
@ConditionalOnNotWebApplication
public TaskRunner taskRunner() {
return new CommandLineTaskRunner();
}
12.3 判断逻辑
Spring Boot 判断"是否为非 Web 应用"的依据是:
- 应用上下文 不是
WebApplicationContext的子类型; - 没有启动内嵌 Web 容器(如 Tomcat、Jetty、Netty);
- 没有引入 Web 相关依赖(如
spring-boot-starter-web或spring-boot-starter-webflux),或者虽引入但未激活 Web 环境(例如通过SpringApplication.setWebApplicationType(WebApplicationType.NONE)显式关闭)。
注意:只要应用被识别为 Servlet Web 或 Reactive Web 应用,
@ConditionalOnNotWebApplication就不会生效。
12.4 典型应用场景
- 命令行应用(CLI 工具)
你开发了一个数据迁移工具,只在命令行运行,不需要 Web 服务器:
java
@SpringBootApplication
public class DataMigrator {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DataMigrator.class);
app.setWebApplicationType(WebApplicationType.NONE); // 显式设为非 Web
app.run(args);
}
}
- 批处理任务(Spring Batch)
在非 Web 环境中启用批处理作业配置。 - 避免 Web 组件污染非 Web 上下文
防止 Web 相关的 Bean(如 Controller、Filter)被错误加载到纯后台服务中。
12.5 与 @ConditionalOnWebApplication 的关系
| 注解 | 生效条件 |
|---|---|
@ConditionalOnWebApplication |
应用是 Web 应用(Servlet 或 Reactive) |
@ConditionalOnNotWebApplication |
应用不是 Web 应用(即 WebApplicationType.NONE) |
二者互斥,常用于提供不同运行环境下的差异化配置。
12.6 注意事项
- 从 Spring Boot 2.0 起,该注解不再支持 type 属性(因为"非 Web"本身就是一种明确状态,无需细分)。
- 如果你的项目同时包含 Web 和非 Web 模块,合理使用此注解可避免 Bean 冲突或不必要的资源加载。
总结
@ConditionalOnNotWebApplication 是 Spring Boot 条件装配体系中的重要一环,专为非 Web 应用场景设计,确保配置和组件只在合适的环境中激活,提升应用的模块化与健壮性。
十三、@ConditionalOnProperty
组合 @Conditional 注解,当指定的属性有指定的值时才开启配置。
@ConditionalOnProperty 是 Spring Boot 提供的一个条件注解(Conditional Annotation) ,用于根据 配置属性(application.properties 或 application.yml 中的属性)的值 来决定是否加载某个 Bean、配置类或组件。
13.1 核心作用
只有当指定的配置属性存在,并且其值满足特定条件时,被注解的类或方法才会生效。
这是实现"按需启用功能"(如开关控制、环境差异化配置)的常用手段。
13.2 基本用法
1.最简形式:检查属性是否存在(不关心值)
java
@ConditionalOnProperty("my.feature.enabled")
只要 my.feature.enabled 在配置文件中有定义 (无论值是 true、false、1、"hello" 等),条件就成立。
⚠️ 注意:如果属性未定义(即完全不存在),条件不成立。
1.检查属性值是否等于指定值
java
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
只有当 my.feature.enabled=true 时才生效(默认 havingValue = "",但通常配合使用)。
3.支持多个属性(逻辑 AND)
@ConditionalOnProperty(
prefix = "my.service",
name = {"host", "port"},
matchIfMissing = false
)
表示 my.service.host 和 my.service.port 都必须存在(且值非空)才生效。
❗ 多个属性之间是 AND 关系,不能直接表达 OR 逻辑(需自定义 Condition)。
13.3 常用属性详解
| 属性 | 说明 |
|---|---|
name / value |
要检查的属性名(支持数组,多个属性需同时满足) |
prefix |
属性前缀,与 name 拼接使用(如 prefix="app.db", name="url" → app.db.url) |
havingValue |
期望的属性值(默认为空字符串)。若设置,则属性值必须完全匹配(区分大小写) |
matchIfMissing |
当属性未定义 时是否匹配,默认 false。设为 true 可实现"默认开启" |
13.4 典型示例
✅ 示例 1:功能开关
properties
# application.properties
app.cache.enabled=true
java
@Configuration
@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new RedisCacheManager();
}
}
只有当
app.cache.enabled=true时,才加载缓存配置。
✅ 示例 2:默认启用(matchIfMissing = true)
java
@ConditionalOnProperty(
name = "app.metrics.enabled",
havingValue = "true",
matchIfMissing = true // 如果没配置,默认视为 true
)
public class MetricsAutoConfiguration {
// ...
}
- 若配置了
app.metrics.enabled=false→ 不加载 - 若未配置该属性 → 加载 (因为
matchIfMissing = true)
常用于"默认开启,可关闭"的场景。
✅ 示例 3:检查多个属性
yaml
# application.yml
my:
datasource:
url: jdbc:mysql://...
username: root
java
@ConditionalOnProperty(
prefix = "my.datasource",
name = {"url", "username"}
)
public class CustomDataSourceConfig {
// 仅当 url 和 username 都配置了才生效
}
13.5 注意事项
- 值匹配是字符串精确匹配
havingValue = "true"不会把"TRUE"或1视为 true,必须是字面量"true"。 - 布尔属性的常见陷阱
Spring Boot 会自动将true/false解析为布尔值,但在条件判断中仍按字符串处理:
properties
feature.x=true # 实际传入 Condition 的是字符串 "true"
所以 havingValue = "true" 是安全的。
- 不支持 OR 条件
如需"propA=xxx或propB=yyy",需自定义@Conditional+Condition实现。 - 优先级
条件判断基于最终生效的配置(包括@PropertySource、环境变量、命令行参数等)。
13.6 底层原理
@ConditionalOnProperty 基于 Spring 的 @Conditional 机制,内部使用 OnPropertyCondition 类进行判断。它会从 Environment 中读取属性值并进行匹配。
13.7 总结
@ConditionalOnProperty 是 Spring Boot 外部化配置 + 条件装配 的核心注解之一,适用于:
- 功能开关(Feature Toggle)
- 环境差异化配置(dev/test/prod)
- 可选模块的自动装配(如监控、日志增强等)
通过灵活组合 name、havingValue 和 matchIfMissing,可以实现强大而清晰的配置驱动行为。
十四、@ConditionalOnExpression
组合 @Conditional 注解,当 SpEL 表达式为 true 时才开启配置。
@ConditionalOnExpression 是 Spring Boot(基于 Spring Framework 的条件机制)提供的一个条件注解(Conditional Annotation) ,它允许你使用 SpEL(Spring Expression Language)表达式 来动态决定某个配置类、Bean 或组件是否应该被加载。
14.1 核心作用
只有当指定的 SpEL 表达式计算结果为 true 时,被注解的类或方法才会生效。
这使得你可以基于任意复杂的逻辑(如多个配置属性组合、系统属性、环境变量、甚至 Bean 状态)来控制配置的启用。
14.2 基本语法
java
@ConditionalOnExpression("#{spel表达式}")
- 表达式必须放在
#{...}中(这是 SpEL 的标准语法)。 - 表达式最终需返回一个 布尔值(boolean)。
- 如果表达式求值为
true→ 条件成立,Bean/配置生效;否则不生效。
14.3 常用场景与示例
✅ 示例 1:基于单个配置属性
properties
# application.properties
app.feature.enabled=true
java
@Configuration
@ConditionalOnExpression("${app.feature.enabled:false}")
public class FeatureConfig {
// 当 app.feature.enabled 为 true 时加载
}
注意:这里
${...}是属性占位符,会被替换为实际值,然后作为布尔表达式求值。
✅ 示例 2:组合多个属性(AND / OR)
properties
app.mode=dev
app.debug=true
java
// 只有在 dev 模式且 debug 开启时才生效
@ConditionalOnExpression(
"${app.mode} == 'dev' and ${app.debug:true}"
)
public class DevToolsConfig { }
或者使用 OR:
java
// dev 或 test 环境都启用
@ConditionalOnExpression(
"'${app.env}' == 'dev' or '${app.env}' == 'test'"
)
public class TestSupportConfig { }
✅ 示例 3:结合系统属性或环境变量
java
// 当系统属性 enable.metrics 为 true 时启用
@ConditionalOnExpression(
"#{systemProperties['enable.metrics'] == 'true'}"
)
public class MetricsConfig { }
// 或检查环境变量
@ConditionalOnExpression(
"#{environment['ENABLE_LOGGING'] == 'true'}"
)
public class LoggingEnhancer { }
systemProperties:Java 系统属性(如-Denable.metrics=true)environment:操作系统环境变量或 Spring Environment 中的所有属性
✅ 示例 4:更复杂的逻辑(字符串判断、非空等)
java
// 当 my.service.url 非空且以 https 开头
@ConditionalOnExpression(
"'${my.service.url:}'.startsWith('https')"
)
public class SecureServiceClient { }
使用
${prop:default}提供默认值(避免属性未定义时报错)。
14.4 注意事项
- 表达式必须返回布尔值
如果表达式返回非布尔类型(如字符串、数字),Spring 会尝试转换,但容易出错。建议显式写成布尔逻辑。 - 属性未定义时可能抛异常
如果直接写${some.prop}而该属性不存在,会抛IllegalArgumentException。
解决方法 :提供默认值,如${some.prop:false}或${some.prop:}。 - 性能影响极小
表达式只在应用启动时求值一次,不影响运行时性能。 - 调试困难
复杂表达式难以调试。建议保持简洁,或封装到自定义Condition中。 - 优先级低于 @Profile
如果同时使用@Profile和@ConditionalOnExpression,两者需同时满足。
14.5 与 @ConditionalOnProperty 的对比
| 特性 | @ConditionalOnProperty |
@ConditionalOnExpression |
|---|---|---|
| 灵活性 | 低(仅支持属性存在性/值匹配) | 高(支持任意 SpEL 表达式) |
| 可读性 | 高(语义清晰) | 中(复杂表达式难读) |
| 安全性 | 高(自动处理属性缺失) | 低(需手动处理默认值) |
| 适用场景 | 简单开关、属性匹配 | 多属性组合、环境判断、复杂逻辑 |
✅ 建议:优先使用 @ConditionalOnProperty ,仅在需要复杂逻辑时用
@ConditionalOnExpression。
14.5 底层原理
@ConditionalOnExpression 基于 Spring 的 @Conditional 机制,内部使用 OnExpressionCondition 类。它通过 StandardEvaluationContext 解析 SpEL 表达式,并注入了以下变量:
environment:当前EnvironmentsystemProperties:System.getProperties()systemEnvironment:System.getenv()
14.6 总结
@ConditionalOnExpression 是 Spring Boot 中最灵活的条件注解之一 ,适用于需要动态、组合、环境感知的配置控制场景。但因其表达式复杂性和潜在的运行时错误,应谨慎使用,保持表达式简洁,并做好默认值处理。
十五、@ConditionalOnJava
组合 @Conditional 注解,当运行的 Java JVM 在指定的版本范围时才开启配置。
@ConditionalOnJava 是 Spring Boot 提供的一个条件注解(Conditional Annotation) ,用于根据 当前运行的 Java 版本 来决定是否加载某个配置类、Bean 或自动配置组件。
15.1 核心作用
只有当 JVM 的 Java 版本满足指定条件(如等于、高于或低于某个版本)时,被注解的类或方法才会生效。
这在需要兼容多个 Java 版本、或利用高版本 Java 特性(如模块系统、新 API)时非常有用。
15.2 基本用法
java
@ConditionalOnJava(JavaVersion.ELEVEN)
表示:仅在 Java 11 环境下生效。
更常见的用法是指定范围:
java
@ConditionalOnJava(range = Range.OLDER_THAN, value = JavaVersion.ELEVEN)
// 表示:Java 版本 < 11 时生效
@ConditionalOnJava(range = Range.AT_LEAST, value = JavaVersion.SIXTEEN)
// 表示:Java 版本 >= 16 时生效
15.3 关键属性说明
| 属性 | 类型 | 说明 |
|---|---|---|
value |
JavaVersion |
目标 Java 版本(必需) |
range |
Range 枚举 |
比较方式,默认为 Range.EQUAL_OR_NEWER(即 ≥) |
JavaVersion 支持的版本(截至 Spring Boot 3.x):
SIX,SEVEN,EIGHT,NINE,TEN,ELEVEN,TWELVE, ...,TWENTY_ONE等- 也支持通过
JavaVersion.valueOf("17")动态解析
⚠️ 注意:Spring Boot 3.x 要求最低 Java 17,因此低版本(如 Java 8)的条件在 SB3 中通常不会命中。
Range 枚举值:
| 值 | 含义 |
|---|---|
EQUAL_OR_NEWER |
≥(默认) |
OLDER_THAN |
< |
ACCEPTED |
==(精确匹配,但实际比较逻辑是"主版本号相等") |
📌 实际比较基于 Java 主版本号(Major Version) ,例如
17.0.2→ 主版本为17。
15.4 典型使用场景
✅ 场景 1:为不同 Java 版本提供不同实现
java
// Java 8~10 使用传统实现
@Bean
@ConditionalOnJava(range = Range.OLDER_THAN, value = JavaVersion.ELEVEN)
public MyService myServiceLegacy() {
return new LegacyMyService();
}
// Java 11+ 使用新特性(如 HttpClient)
@Bean
@ConditionalOnJava(range = Range.AT_LEAST, value = JavaVersion.ELEVEN)
public MyService myServiceModern() {
return new ModernMyService();
}
✅ 场景 2:禁用不兼容高版本 Java 的旧功能
java
@Configuration
@ConditionalOnJava(range = Range.OLDER_THAN, value = JavaVersion.SEVENTEEN)
public class LegacySecurityConfig {
// 仅在 Java < 17 时加载(因 Java 17 移除了某些加密算法)
}
✅ 场景 3:Spring Boot 自动配置中的兼容处理
Spring Boot 内部大量使用此注解,例如:
- 在 Java 9+ 启用模块路径相关配置
- 在 Java 14+ 启用 Record 支持(未来可能)
15.5 注意事项
- 版本检测基于运行时 JVM
与编译版本无关,只看java -version的实际运行环境。 - 主版本号比较
JavaVersion.ELEVEN对应主版本11,11.0.12、11.0.2都视为11。 - Spring Boot 版本限制
- Spring Boot 2.x:支持 Java 8 ~ 19
- Spring Boot 3.x:最低要求 Java 17 ,因此
@ConditionalOnJava(value = JavaVersion.EIGHT)在 SB3 中永远不会生效。
- 不支持补丁版本精细控制
无法判断17.0.1vs17.0.2,仅支持主版本(如 17、18、19...)。
15.6 底层原理
@ConditionalOnJava 基于 @Conditional(OnJavaCondition.class) 实现。
OnJavaCondition 会:
- 获取当前 JVM 版本(通过
System.getProperty("java.version")) - 解析为主版本号(如
"17.0.5" → 17) - 与
value指定的版本按range进行比较
15.7 替代方案(手动判断)
如果需要更灵活的版本判断(如检查是否 ≥ Java 17 且 < Java 21),可结合 @ConditionalOnExpression:
java
@ConditionalOnExpression(
"#{T(java.lang.Runtime).version().feature() >= 17 && " +
" T(java.lang.Runtime).version().feature() < 21}"
)
使用
Runtime.version()(Java 9+ 引入)可获取精确版本信息。
15.8 总结
@ConditionalOnJava 是 Spring Boot 中用于 Java 版本兼容性控制 的重要工具,适用于:
- 多版本 Java 环境下的差异化配置
- 利用高版本 Java 新特性
- 规避旧版 JVM 的限制或安全问题
合理使用它,可以让你的库或应用在不同 Java 环境中安全、高效、自动适配。