Spring **${}** vs **#{}** 语法全景图

Spring ${} vs #{} 语法全景图

一句话记忆:
${}占位符(静态字符串替换),#{}SpEL 表达式(运行时计算)。
二者可以 嵌套联动,带来 声明式动态能力


一、基础区别

维度${}#{}
名称Property PlaceholderSpEL(Spring Expression Language)
求值时机容器启动时 替换运行时 解析
数据来源Environment、PropertySourceBean、系统属性、运算、方法调用
常见用途读取 application.yml计算、条件、集合、Bean 调用
示例${server.port}#{T(java.lang.Math).random()}
默认值${key:default}#{key ?: default}

二、基础用法对比

1️⃣ 读取配置

my:
  name: Tom
  age: 18
@Value("${my.name}")           // Tom
private String name;

@Value("${my.age}")            // 18(字符串)
private int age;

@Value("${unknown:default}")   // default
private String fallback;

2️⃣ 运行时计算

@Value("#{T(java.lang.Math).random() * 100}")
private double rand100;

@Value("#{systemProperties['os.name']}")
private String os;

三、混合嵌套(经典技巧)

@Value("#{${feature.price} * (1 + ${feature.tax})}")
private BigDecimal finalPrice;
  • ${feature.price} 先被替换为 100,表达式变为 #{100 * (1 + 0.2)}120

四、高级用法速查表

场景代码示例
三目/Elvis"#{flag ? 'A' : 'B'}" / "#{name ?: 'NO_NAME'}"
集合索引"${list[0]}" / "#{map['key']}"
集合过滤 & 投影"#{users.?[age > 18].![name]}"
正则匹配"#{email matches '^[\\w\\.-]+@(.+)$'}"
Bean 调用"#{priceService.current() * 1.1}"
静态方法"#{T(java.time.LocalDate).now()}"
条件 Bean 注册@ConditionalOnExpression("#{env['spring.profiles.active'] == 'prod'}")

五、XML / 注解 / 编程式 全场景

场景写法
XML<property name="timeout" value="#{${timeout} * 1000}"/>
@Bean@Bean @ConditionalOnExpression("#{systemProperties['debug'] != null}")
编程式ExpressionParser parser = new SpelExpressionParser();

六、常见误区 & 避坑

说明
${} 不支持运算${1+2} 会原样输出,不会被计算
#{}application.yml 无效只能在 @ValueXML@Conditional 等场景使用
构造器注入 无法实时刷新使用 @ConfigurationProperties 或字段注入
默认值写法${key:default} vs #{key ?: default}

七、一句话总结

${} 负责“占位”,#{} 负责“计算”;
二者嵌套即可实现声明式动态配置,让 Spring 在运行时依旧“活”起来。

### AOP术语及相关概念 #### 1. 面向切面编程(AOP) 面向切面编程是一种编程范式,旨在将横切关注点(Cross-Cutting Concerns)与业务逻辑分离。这种设计模式使得开发者能够在不修改原始代码的前提下增强程序的功能[^1]。 --- #### 2. 核心术语解释 ##### 切面(Aspect) 切面是模块化的单元,用于封装横切关注点的代码。它可以看作是一个容器,其中包含了通知和切点定义。在 Spring 中,可以通过 `@Aspect` 注解来标记一个类为切面[^2]。 ##### 通知(Advice) 通知是在特定连接点执行的动作或行为。Spring 支持五种主要的通知类型:前置通知 (`Before`)、后置通知 (`After Returning`)、异常通知 (`After Throwing`)、最终通知 (`After (finally)`) 和环绕通知 (`Around`)。这些通知可以使用相应的注解(如 `@Before`, `@AfterReturning` 等)进行配置[^2]。 ##### 切点(Pointcut) 切点是用来定位连接点的一组规则或表达式。它决定了哪些方法会被拦截并应用通知。Spring 提供了一套强大的表达式语法来定义切点,例如基于包名、类名或方法签名的选择器[^2]。 ##### 连接点(Join Point) 连接点是指程序执行过程中的某个具体位置,比如方法调用、字段访问或者异常抛出等。它是 AOP 的基本操作单位,在运行时由框架识别并触发相应的行为。 ##### 目标对象(Target Object) 目标对象指的是被代理的对象实例,也就是那些需要被增强的实际业务组件。如果采用 JDK 动态代理,则该对象必须实现至少一个接口;而 CGLIB 则可以直接扩展未提供显式接口的目标类。 ##### 代理对象(Proxy Object) 为了实现代理机制,AOP 创建了一个新的中间层——即所谓的“代理”。这个代理负责拦截对目标对象的方法调用,并在其前后插入额外处理逻辑。根据实际情况的不同,可以选择不同的技术手段创建代理,如前面提到过的两种方式之一。 --- #### 3. AOP 全景图概述 以下是关于 AOP 工作流的一个简化版全景视图: | **客户端请求** | → | **代理对象** | → | **切面(包含多个通知)** | ↔️ | **目标对象** | |------------------|---|----------------|---|----------------------------|----|---------------| 在这个过程中: - 客户端并不直接与目标对象交互,而是通过代理对象完成所有的通信。 - 当某次调用满足预设条件时,对应的切点会激活关联的通知逻辑。 - 所有这一切都发生在幕后,对于外部使用者而言完全透明化。 此外,还需要注意的是,当决定如何构建合适的代理结构时,通常遵循如下原则: ```java if (targetClass.implementsInterface()) { useJdkDynamicProxy(); } else { generateCglibSubclassProxy(); } ``` 此段伪代码展示了依据是否存在已知接口这一特性来进行决策的过程[^2]。 --- ### 示例代码展示 下面给出一段简单的例子演示如何利用 Spring 实现基础的日志记录功能: ```java // 声明一个切面 @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") // 定义切点 public void logMethodEntry(JoinPoint joinPoint) { System.out.println("Entering method: " + joinPoint.getSignature().getName()); } } @Service public class MyService { public String performTask() { return "Task completed!"; } } ``` 在这里我们看到,每当任何属于指定包路径下的服务类成员函数被执行之前都会打印一条消息到控制台输出窗口中去[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半部论语

如果觉得有帮助,打赏鼓励一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值