spring #{} 与 ${} 区别

${} 与 #{} 的核心区别

特性 ${} #{}
名称 属性占位符 SpEL表达式
解析时机 启动时/类加载时 运行时
执行引擎 Spring属性解析器 Spring表达式语言(SpEL)
主要用途 读取配置文件 执行表达式/方法调用
动态计算 静态值替换 动态计算
默认值 支持 ${key:default} 需要在内部使用${}

1. ${} - 属性占位符

功能:从Spring Environment、properties文件、YAML文件中读取配置值

java 复制代码
@Component
@RocketMQMessageListener(
    topic = "${rocketmq.topic}",           // 从配置文件读取
    consumerGroup = "${rocketmq.group:default-group}" // 带默认值
)

YAML配置:

bash 复制代码
rocketmq:
  topic: test-topic
  group: test-group

特点:

  • ✅ 只能做静态值替换

  • ✅ 支持默认值 ${key:default}

  • ✅ 不支持运算、方法调用

  • ✅ 解析发生在依赖注入阶段


2. #{} - SpEL表达式

功能:执行Java代码、调用方法、访问Bean属性、进行运算

java 复制代码
@Component
@RocketMQMessageListener(
    // 调用Bean的方法
    topic = "#{@topicConfig.getTopic()}",
    
    // 访问Bean属性
    consumerGroup = "#{@consumerProperties.group}",
    
    // 三目运算符
    selectorExpression = "#{@config.env == 'prod' ? 'tag1' : 'tag2'}"
)

支持的表达式:

java 复制代码
// 调用静态方法
#{T(System).currentTimeMillis()}

// 字符串操作
#{'${rocketmq.topic}'.toUpperCase()}

// 条件运算
#{@conditionService.isEnable() ? 'topicA' : 'topicB'}

// 数学运算
#{${server.port:8080} + 1}

3. 两者组合使用

java 复制代码
@Component
@RocketMQMessageListener(
    // ${} 作为 #{} 的参数
    topic = "#{'${rocketmq.topic:default}'.toUpperCase()}",
    
    // 读取配置并进行运算
    consumerGroup = "#{'group-' + '${rocketmq.group:default}'}",
    
    // 复杂逻辑
    selectorExpression = "#{@tagService.getTags('${rocketmq.biz.type}')}"
)

4. 典型应用场景对比

场景 推荐方式 示例
读取简单配置 ${} ${rocketmq.topic}
配置默认值 ${} ${rocketmq.group:default}
调用Bean方法 #{} #{@service.method()}
字符串处理 #{} #{'test'.toUpperCase()}
条件判断 #{} #{@env == 'dev' ? A : B}
访问数组/集合 #{} #{@config.list[0]}
配置文件转大写 ${} + #{} #{'${topic}'.toUpperCase()}

5. 注解中使用的限制

Spring注解中 (如@Value、@RocketMQMessageListener):

  • ${} - 直接支持,自动解析配置文件

  • #{} - 需要配置spring.factories或特定版本才支持

java 复制代码
// ✅ 直接工作
@Value("${rocketmq.topic}")

// ⚠️ 部分注解不支持#{},或需要特殊处理
@RocketMQMessageListener(topic = "#{...}")  // 某些版本可能不解析

6. 最佳实践建议

简单场景:只用${}

java 复制代码
@RocketMQMessageListener(
    topic = "${rocketmq.topic}",
    consumerGroup = "${rocketmq.group}"
)

复杂场景:在#{}内嵌${}

java 复制代码
@RocketMQMessageListener(
    topic = "#{'${rocketmq.topic}'.trim()}",
    consumerGroup = "#{@environment.getProperty('rocketmq.group')}"
)

无法使用#{}时:通过中间Bean

java 复制代码
@Component
public class TopicProvider {
    @Value("${rocketmq.topic}")
    private String topic;
    
    public String getTopic() {
        return topic.toUpperCase(); // 复杂处理
    }
}

// 使用时
@RocketMQMessageListener(
    topic = "#{@topicProvider.getTopic()}"
)

总结

  • ${}:读配置,静态替换,简单直接 ✅

  • #{}:执行代码,动态计算,功能强大 ⚡

  • 组合使用 :#{'${key}'.method()} 最灵活 💪

相关推荐
投图匠7 小时前
十种不同风格的 CTP接口 框架
开发语言·python
2601_967980119 小时前
深入理解词向量算法及其在Python中的实践
python·word2vec·glove·fasttext·词向量
129Lab9 小时前
电池循环测试数据自动化分析:用Python+AI工具构建一键式寿命评估流水线
python·数据分析·电池测试·数据自动化·寿命评估
treasure1409 小时前
【复古】浅尝Django前后端不分离
前端·css·python·django·bootstrap·html·web
Dmz丶10 小时前
前端转 AI 100 天 Day 44:前端集成实战——用 React 给 Agent 做个正式界面
python
jay神10 小时前
基于 YOLOv8 的手势识别系统(Web 版)
前端·人工智能·python·深度学习·yolo·计算机视觉·计算机毕业设计
DeepVisionary10 小时前
阿里云上线旗舰多模态新模型,不公布跑分且限时14天退役
python·自动化
东莞市云毅网络有限公司10 小时前
FAQ 覆盖度打分:TF-IDF 与向量嵌入双路召回的 Python 实现
python·数据清洗·rag·企业知识库·文档解析
爱笑的k1111 小时前
nano-vllm 源码剖析:千行 Python 手写离线推理引擎的调度、显存与并行时序
开发语言·python·vllm
wang_shu_mo_ran11 小时前
MyBatis 从入门到进阶(一)
java·spring·mybatis