在Java中解析Spring占位符,模拟@Value注解功能
在Java代码中实现@Value注解的功能
我之前也踩过这个坑,用SpelExpressionParser和Environment没成功多半是没把两者的逻辑结合对——毕竟@Value的底层就是先解析${}格式的环境占位符,再处理#{}包裹的SpEL表达式。下面给你一套可行的实现方案:
核心思路
@Value的工作流程分两步:
- 先替换
${property.name}这类占位符,从Spring环境(Environment)中读取对应属性值 - 再解析剩余的
#{spel.expression}部分,执行SpEL表达式计算结果
咱们就按照这个流程来写代码,用Spring的公开API完成复刻。
完整实现代码
首先创建一个组件类,注入Environment并封装解析逻辑:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.expression.StandardEvaluationContext; import org.springframework.core.env.Environment; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.TemplateParserContext; import org.springframework.stereotype.Component; @Component public class ManualValueResolver { private final Environment environment; private final ExpressionParser spelParser; private final TemplateParserContext templateContext; // 用构造注入保证Environment已初始化 @Autowired public ManualValueResolver(Environment environment) { this.environment = environment; this.spelParser = new SpelExpressionParser(); // TemplateParserContext专门用来解析"xxx #{expr} yyy"这种模板式SpEL this.templateContext = new TemplateParserContext(); } public String resolveValue(String expression) { // 第一步:解析${}占位符,从环境中获取属性值 String resolvedPlaceholders = environment.resolveRequiredPlaceholders(expression); // 第二步:解析#{}格式的SpEL表达式,把Environment作为根对象方便SpEL访问环境属性 return spelParser.parseExpression(resolvedPlaceholders, templateContext) .getValue(new StandardEvaluationContext(environment), String.class); } }
使用示例
假设你的application.properties里有app.name=MySpringApp,调用这个方法:
// 注入咱们写的解析器 @Autowired private ManualValueResolver valueResolver; public void testResolver() { String result = valueResolver.resolveValue("应用名称:${app.name},当前时间:#{T(java.time.LocalDateTime).now()}"); System.out.println(result); // 输出类似:应用名称:MySpringApp,当前时间:2024-05-20T14:30:00.123 }
常见问题排查
如果之前尝试失败,可能是这几个原因:
- 没有用
TemplateParserContext:默认的SpEL解析器只能处理纯表达式(比如直接写T(java.time.LocalDate).now()),无法解析带#{}包裹的模板字符串 - 没先解析
${}占位符:直接用SpEL解析包含${}的字符串会报错,因为SpEL不认识这种格式 - 初始化时机不对:在Spring容器完全启动前调用解析逻辑,
Environment还没加载完属性
内容的提问来源于stack exchange,提问作者Bill
相关产品推荐
相关产品推荐

