十一、@ConditionalOnWebApplication
组合 @Conditional 注解,当前项目类型是 WEB 项目才开启配置。
当前项目有以下 3 种类型。
@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Conditional({OnWebApplicationCondition.class})public@interfaceConditionalOnWebApplication{Typetype()defaultConditionalOnWebApplication.Type.ANY;publicstaticenumType{ANY,SERVLET,REACTIVE;privateType(){}}}11.1 作用
该注解的作用是:只有当 Spring 应用上下文是一个 Web 应用上下文时,被注解的类或方法才会被注册为 Bean 或生效。
这在自动配置(Auto-Configuration)中非常常见,用于区分 Web 应用和非 Web 应用(如批处理、命令行工具等)。
11.2 使用方式
11.2.1 注解在类上(通常用于@Configuration类)
@Configuration@ConditionalOnWebApplicationpublicclassWebConfig{// 只有在 Web 应用中才会加载这个配置类}11.2.2 注解在@Bean方法上
@Bean@ConditionalOnWebApplicationpublicMyWebBeanmyWebBean(){returnnewMyWebBean();}11.3 可选类型(type 属性)
从 Spring Boot 2.0 开始,@ConditionalOnWebApplication支持通过type属性进一步指定 Web 应用的类型:
@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
@Configuration@ConditionalOnWebApplication(type=Type.SERVLET)publicclassServletWebConfig{// 仅在 Servlet 环境下生效}@Configuration@ConditionalOnWebApplication(type=Type.REACTIVE)publicclassReactiveWebConfig{// 仅在 Reactive 环境下生效}11.7 总结
@ConditionalOnWebApplication是 Spring Boot 条件化配置的重要工具之一,能帮助开发者根据应用类型动态启用或禁用某些配置,从而提升模块的通用性和灵活性。
十二、@ConditionalOnNotWebApplication
组合 @Conditional 注解,和 @ConditionalOnWebApplication 注解相反,当前项目类型不是 WEB 项目才开启配置。
12.1 作用
该注解用于仅在当前应用不是一个 Web 应用时,才加载被注解的配置类、Bean 或组件。
换句话说:
✅只有当应用是“非 Web 应用”(如命令行工具、批处理任务、后台服务等)时,该配置才会生效。
12.2 使用方式
12.2.1 注解在配置类上
@Configuration@ConditionalOnNotWebApplicationpublicclassBatchConfig{// 仅在非 Web 应用中加载此配置}12.2.2 注解在@Bean方法上
@Bean@ConditionalOnNotWebApplicationpublicTaskRunnertaskRunner(){returnnewCommandLineTaskRunner();}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 服务器:
@SpringBootApplicationpublicclassDataMigrator{publicstaticvoidmain(String[]args){SpringApplicationapp=newSpringApplication(DataMigrator.class);app.setWebApplicationType(WebApplicationType.NONE);// 显式设为非 Webapp.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.最简形式:检查属性是否存在(不关心值)
@ConditionalOnProperty("my.feature.enabled")只要my.feature.enabled在配置文件中有定义(无论值是true、false、1、"hello"等),条件就成立。
⚠️ 注意:如果属性未定义(即完全不存在),条件不成立。
1.检查属性值是否等于指定值
@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:功能开关
# application.properties app.cache.enabled=true@Configuration@ConditionalOnProperty(name="app.cache.enabled",havingValue="true")publicclassCacheConfig{@BeanpublicCacheManagercacheManager(){returnnewRedisCacheManager();}}只有当
app.cache.enabled=true时,才加载缓存配置。
✅ 示例 2:默认启用(matchIfMissing = true)
@ConditionalOnProperty(name="app.metrics.enabled",havingValue="true",matchIfMissing=true// 如果没配置,默认视为 true)publicclassMetricsAutoConfiguration{// ...}- 若配置了
app.metrics.enabled=false→ 不加载 - 若未配置该属性 →加载(因为
matchIfMissing = true)
常用于“默认开启,可关闭”的场景。
✅ 示例 3:检查多个属性
# application.ymlmy:datasource:url:jdbc:mysql://...username:root@ConditionalOnProperty(prefix="my.datasource",name={"url","username"})publicclassCustomDataSourceConfig{// 仅当 url 和 username 都配置了才生效}13.5 注意事项
- 值匹配是字符串精确匹配
havingValue = "true"不会把"TRUE"或1视为 true,必须是字面量"true"。 - 布尔属性的常见陷阱
Spring Boot 会自动将true/false解析为布尔值,但在条件判断中仍按字符串处理:
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 基本语法
@ConditionalOnExpression("#{spel表达式}")- 表达式必须放在
#{...}中(这是 SpEL 的标准语法)。 - 表达式最终需返回一个布尔值(boolean)。
- 如果表达式求值为
true→ 条件成立,Bean/配置生效;否则不生效。
14.3 常用场景与示例
✅ 示例 1:基于单个配置属性
# application.properties app.feature.enabled=true@Configuration@ConditionalOnExpression("${app.feature.enabled:false}")publicclassFeatureConfig{// 当 app.feature.enabled 为 true 时加载}注意:这里
${...}是属性占位符,会被替换为实际值,然后作为布尔表达式求值。
✅ 示例 2:组合多个属性(AND / OR)
app.mode=dev app.debug=true// 只有在 dev 模式且 debug 开启时才生效@ConditionalOnExpression("${app.mode} == 'dev' and ${app.debug:true}")publicclassDevToolsConfig{}或者使用 OR:
// dev 或 test 环境都启用@ConditionalOnExpression("'${app.env}' == 'dev' or '${app.env}' == 'test'")publicclassTestSupportConfig{}✅ 示例 3:结合系统属性或环境变量
// 当系统属性 enable.metrics 为 true 时启用@ConditionalOnExpression("#{systemProperties['enable.metrics'] == 'true'}")publicclassMetricsConfig{}// 或检查环境变量@ConditionalOnExpression("#{environment['ENABLE_LOGGING'] == 'true'}")publicclassLoggingEnhancer{}
systemProperties:Java 系统属性(如-Denable.metrics=true)environment:操作系统环境变量或 Spring Environment 中的所有属性
✅ 示例 4:更复杂的逻辑(字符串判断、非空等)
// 当 my.service.url 非空且以 https 开头@ConditionalOnExpression("'${my.service.url:}'.startsWith('https')")publicclassSecureServiceClient{}使用
${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 基本用法
@ConditionalOnJava(JavaVersion.ELEVEN)表示:仅在 Java 11 环境下生效。
更常见的用法是指定范围:
@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 8~10 使用传统实现@Bean@ConditionalOnJava(range=Range.OLDER_THAN,value=JavaVersion.ELEVEN)publicMyServicemyServiceLegacy(){returnnewLegacyMyService();}// Java 11+ 使用新特性(如 HttpClient)@Bean@ConditionalOnJava(range=Range.AT_LEAST,value=JavaVersion.ELEVEN)publicMyServicemyServiceModern(){returnnewModernMyService();}✅ 场景 2:禁用不兼容高版本 Java 的旧功能
@Configuration@ConditionalOnJava(range=Range.OLDER_THAN,value=JavaVersion.SEVENTEEN)publicclassLegacySecurityConfig{// 仅在 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:
@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 环境中安全、高效、自动适配。