Environment.getProperty()性能优化
Environment.getProperty()性能优化
·
Environment.getProperty()
-
问题背景:在 HTTP 过滤器、拦截器等高频调用场景中,直接使用
environment.getProperty("key")获取配置属性会带来性能损耗:- 属性源遍历开销:每次调用需遍历
PropertySources查找键值 - 重复计算:若属性值固定(如
spring.application.name),重复调用浪费资源
- 属性源遍历开销:每次调用需遍历
-
典型场景:
- 过滤器或拦截器中设置 HTTP 响应头时频繁读取固定配置。
- 微服务调用链中传递应用名、版本号等元数据。
-
性能问题分析
1、原始实现
public class XdrHttpResponseFilter extends OncePerRequestFilter {
@Autowired
private Environment environment;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 每次请求调用 getProperty,存在性能隐患
setHeader(response, "X-App-Name", environment.getProperty("spring.application.name"));
setHeader(response, "X-App-Version", environment.getProperty("xdr.monitor.application.version"));
filterChain.doFilter(request, response);
}
private void setHeader(HttpServletResponse response, String key, String value) {
if (StringUtils.hasText(value)) {
response.setHeader(key, value);
}
}
}
性能问题:
- 高频属性查找:每个 HTTP 请求触发 2 次
getProperty调用。 - 属性源遍历:若配置源较多(如多配置文件、远程配置中心),查找耗时增加。
2、优化方案
-
缓存配置值,在初始化阶段一次性读取配置,避免重复调用
getProperty: -
优化后的实现:
public class XdrHttpResponseFilter extends OncePerRequestFilter {
private String appName;
private String appVersion;
@Autowired
private Environment environment;
@Override
protected void initFilterBean() {
// 初始化阶段一次性读取配置
this.appName = environment.getProperty("spring.application.name");
this.appVersion = environment.getProperty("xdr.monitor.application.version");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 直接使用缓存值
setHeader(response, "X-App-Name", appName);
setHeader(response, "X-App-Version", appVersion);
filterChain.doFilter(request, response);
}
private void setHeader(HttpServletResponse response, String key, String value) {
if (StringUtils.hasText(value)) {
response.setHeader(key, value);
}
}
}
优化关键点:
-
缓存配置值:在
initFilterBean生命周期阶段读取并缓存配置。 -
线程安全:配置值通常不变,无需同步。
测试验证:JMH 基准测试
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Fork(2)
@Warmup(iterations = 3, time = 2)
@Measurement(iterations = 5, time = 5)
@Threads(8) // 模拟 8 线程并发
public class EnvironmentGetPropertyBenchmark {
private Environment environment;
private String appName;
private String appVersion;
@Setup
public void setup() {
// 初始化 MockEnvironment
MockEnvironment env = new MockEnvironment();
env.setProperty("spring.application.name", "my-service");
env.setProperty("xdr.monitor.application.version", "1.0.0");
this.environment = env;
// 优化版本预读取配置
this.appName = env.getProperty("spring.application.name");
this.appVersion = env.getProperty("xdr.monitor.application.version");
}
/** 原始实现:每次调用 getProperty */
@Benchmark
public void testOriginal(Blackhole bh) {
bh.consume(environment.getProperty("spring.application.name"));
bh.consume(environment.getProperty("xdr.monitor.application.version"));
}
/** 优化实现:使用缓存值 */
@Benchmark
public void testOptimized(Blackhole bh) {
bh.consume(appName);
bh.consume(appVersion);
}
}
- 测试结果
Benchmark Mode Cnt Score Error Units
TT.EnvironmentGetPropertyBenchmark.testOptimized thrpt 10 1335706.702 ± 100309.134 ops/ms
TT.EnvironmentGetPropertyBenchmark.testOriginal thrpt 10 20.171 ± 0.972 ops/ms
-
结论:优化后性能是原始版本的 66,200 倍。
-
上述缓存的形式也可以换成 ConcurrentHashMap 来缓存
建议优化场景
- HTTP 过滤器/拦截器:每个请求均触发
getProperty - 循环体内部:避免在循环中重复调用
getProperty - 需要频繁读取固定配置的场景(如服务名、版本号、开关配置)
更多推荐


所有评论(0)