springboot注解(三)

十一、@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 应用:是否存在 DispatcherServletServletContext
  • 对于 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-webspring-boot-starter-webflux),或者虽引入但未激活 Web 环境(例如通过 SpringApplication.setWebApplicationType(WebApplicationType.NONE) 显式关闭)。

注意:只要应用被识别为 Servlet WebReactive 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 在配置文件中有定义 (无论值是 truefalse1"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.hostmy.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 注意事项

  1. 值匹配是字符串精确匹配
    havingValue = "true" 不会把 "TRUE"1 视为 true,必须是字面量 "true"
  2. 布尔属性的常见陷阱
    Spring Boot 会自动将 true/false 解析为布尔值,但在条件判断中仍按字符串处理
properties 复制代码
   feature.x=true   # 实际传入 Condition 的是字符串 "true"

所以 havingValue = "true" 是安全的。

  1. 不支持 OR 条件
    如需"propA=xxxpropB=yyy",需自定义 @Conditional + Condition 实现。
  2. 优先级
    条件判断基于最终生效的配置(包括 @PropertySource、环境变量、命令行参数等)。

13.6 底层原理

@ConditionalOnProperty 基于 Spring 的 @Conditional 机制,内部使用 OnPropertyCondition 类进行判断。它会从 Environment 中读取属性值并进行匹配。


13.7 总结

@ConditionalOnProperty 是 Spring Boot 外部化配置 + 条件装配 的核心注解之一,适用于:

  • 功能开关(Feature Toggle)
  • 环境差异化配置(dev/test/prod)
  • 可选模块的自动装配(如监控、日志增强等)

通过灵活组合 namehavingValuematchIfMissing,可以实现强大而清晰的配置驱动行为。

十四、@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 注意事项

  1. 表达式必须返回布尔值
    如果表达式返回非布尔类型(如字符串、数字),Spring 会尝试转换,但容易出错。建议显式写成布尔逻辑。
  2. 属性未定义时可能抛异常
    如果直接写 ${some.prop} 而该属性不存在,会抛 IllegalArgumentException
    解决方法 :提供默认值,如 ${some.prop:false}${some.prop:}
  3. 性能影响极小
    表达式只在应用启动时求值一次,不影响运行时性能。
  4. 调试困难
    复杂表达式难以调试。建议保持简洁,或封装到自定义 Condition 中。
  5. 优先级低于 @Profile
    如果同时使用 @Profile@ConditionalOnExpression,两者需同时满足。

14.5 与 @ConditionalOnProperty 的对比

特性 @ConditionalOnProperty @ConditionalOnExpression
灵活性 低(仅支持属性存在性/值匹配) 高(支持任意 SpEL 表达式)
可读性 高(语义清晰) 中(复杂表达式难读)
安全性 高(自动处理属性缺失) 低(需手动处理默认值)
适用场景 简单开关、属性匹配 多属性组合、环境判断、复杂逻辑

✅ 建议:优先使用 @ConditionalOnProperty ,仅在需要复杂逻辑时用 @ConditionalOnExpression

14.5 底层原理

@ConditionalOnExpression 基于 Spring 的 @Conditional 机制,内部使用 OnExpressionCondition 类。它通过 StandardEvaluationContext 解析 SpEL 表达式,并注入了以下变量:

  • environment:当前 Environment
  • systemPropertiesSystem.getProperties()
  • systemEnvironmentSystem.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 注意事项

  1. 版本检测基于运行时 JVM
    与编译版本无关,只看 java -version 的实际运行环境。
  2. 主版本号比较
    JavaVersion.ELEVEN 对应主版本 1111.0.1211.0.2 都视为 11
  3. Spring Boot 版本限制
    • Spring Boot 2.x:支持 Java 8 ~ 19
    • Spring Boot 3.x:最低要求 Java 17 ,因此 @ConditionalOnJava(value = JavaVersion.EIGHT) 在 SB3 中永远不会生效。
  4. 不支持补丁版本精细控制
    无法判断 17.0.1 vs 17.0.2,仅支持主版本(如 17、18、19...)。

15.6 底层原理

@ConditionalOnJava 基于 @Conditional(OnJavaCondition.class) 实现。
OnJavaCondition 会:

  1. 获取当前 JVM 版本(通过 System.getProperty("java.version")
  2. 解析为主版本号(如 "17.0.5" → 17
  3. 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 环境中安全、高效、自动适配

相关推荐
树码小子4 分钟前
Spring框架:Spring程序快速上手
java·后端·spring
李松桃9 分钟前
python第三次作业
java·前端·python
马士兵教育11 分钟前
计算机专业学生入行IT行业,编程语言如何选择?
java·开发语言·c++·人工智能·python
本妖精不是妖精16 分钟前
搭建 JNI 开发环境:使用 IntelliJ IDEA 和 CLion
java
老毛肚24 分钟前
uniapp-ruoyi-spring部署宝塔
java·spring·uni-app
砚边数影27 分钟前
决策树实战:基于 KingbaseES 的鸢尾花分类 —— 模型可视化输出
java·数据库·决策树·机器学习·分类·金仓数据库
夕除30 分钟前
js--6
java·开发语言
手握风云-1 小时前
JavaEE 进阶第十三期:Spring Ioc & DI,从会用容器到成为容器(下)
java·spring·java-ee
组合缺一1 小时前
论 AI Skills 分布式发展的必然性:从单体智能到“云端大脑”的跃迁
java·人工智能·分布式·llm·mcp·skills
砚边数影1 小时前
决策树原理(一):信息增益与特征选择 —— Java 实现 ID3 算法
java·数据库·决策树·机器学习·kingbase·数据库平替用金仓·金仓数据库