• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.77
today-context/src/main/java/infra/context/expression/StandardBeanExpressionResolver.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.context.expression;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.util.concurrent.ConcurrentHashMap;
23

24
import infra.beans.BeansException;
25
import infra.beans.factory.BeanExpressionException;
26
import infra.beans.factory.config.BeanExpressionContext;
27
import infra.beans.factory.config.BeanExpressionResolver;
28
import infra.core.conversion.ConversionService;
29
import infra.expression.Expression;
30
import infra.expression.ExpressionParser;
31
import infra.expression.ParserContext;
32
import infra.expression.spel.SpelParserConfiguration;
33
import infra.expression.spel.standard.SpelExpressionParser;
34
import infra.expression.spel.support.StandardEvaluationContext;
35
import infra.expression.spel.support.StandardTypeConverter;
36
import infra.expression.spel.support.StandardTypeLocator;
37
import infra.format.support.ApplicationConversionService;
38
import infra.lang.Assert;
39
import infra.lang.TodayStrategies;
40
import infra.util.StringUtils;
41

42
/**
43
 * Standard implementation of the {@link BeanExpressionResolver} interface,
44
 * parsing and evaluating EL using {@code infra.expression} module.
45
 *
46
 * <p>All beans in the containing {@code BeanFactory} are made available as
47
 * predefined variables with their common bean name, including standard context
48
 * beans such as "environment", "systemProperties" and "systemEnvironment".
49
 *
50
 * @author Juergen Hoeller
51
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
52
 * @see BeanExpressionContext#beanFactory
53
 * @see ExpressionParser
54
 * @see SpelExpressionParser
55
 * @see StandardEvaluationContext
56
 * @since 4.0 2021/12/25 15:01
57
 */
58
public class StandardBeanExpressionResolver implements BeanExpressionResolver, ParserContext {
59

60
  /**
61
   * System property to configure the maximum length for SpEL expressions: {@value}.
62
   * <p>Can also be configured via the {@link TodayStrategies} mechanism.
63
   *
64
   * @see SpelParserConfiguration#getMaximumExpressionLength()
65
   */
66
  public static final String MAX_SPEL_EXPRESSION_LENGTH_PROPERTY_NAME = "spel.context.max-length";
67

68
  /** Default expression prefix: "#{". */
69
  public static final String DEFAULT_EXPRESSION_PREFIX = "#{";
70

71
  /** Default expression suffix: "}". */
72
  public static final String DEFAULT_EXPRESSION_SUFFIX = "}";
73

74
  private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX;
6✔
75

76
  private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
6✔
77

78
  private ExpressionParser expressionParser;
79

80
  private final ConcurrentHashMap<String, Expression> expressionCache = new ConcurrentHashMap<>(256);
12✔
81

82
  private final ConcurrentHashMap<BeanExpressionContext, StandardEvaluationContext> evaluationCache = new ConcurrentHashMap<>(8);
12✔
83

84
  /**
85
   * Create a new {@code StandardBeanExpressionResolver} with default settings.
86
   */
87
  public StandardBeanExpressionResolver() {
2✔
88
    this.expressionParser = SpelExpressionParser.INSTANCE;
3✔
89
  }
1✔
90

91
  /**
92
   * Create a new {@code StandardBeanExpressionResolver} with the given bean class loader,
93
   * using it as the basis for expression compilation.
94
   *
95
   * @param beanClassLoader the factory's bean class loader
96
   */
97
  public StandardBeanExpressionResolver(@Nullable ClassLoader beanClassLoader) {
2✔
98
    SpelParserConfiguration parserConfig = new SpelParserConfiguration(
7✔
99
            null, beanClassLoader, false, false, Integer.MAX_VALUE, retrieveMaxExpressionLength());
3✔
100
    this.expressionParser = new SpelExpressionParser(parserConfig);
6✔
101
  }
1✔
102

103
  /**
104
   * Set the prefix that an expression string starts with.
105
   * The default is "#{".
106
   *
107
   * @see #DEFAULT_EXPRESSION_PREFIX
108
   */
109
  public void setExpressionPrefix(String expressionPrefix) {
110
    Assert.hasText(expressionPrefix, "Expression prefix must not be empty");
3✔
111
    this.expressionPrefix = expressionPrefix;
3✔
112
  }
1✔
113

114
  /**
115
   * Set the suffix that an expression string ends with.
116
   * The default is "}".
117
   *
118
   * @see #DEFAULT_EXPRESSION_SUFFIX
119
   */
120
  public void setExpressionSuffix(String expressionSuffix) {
121
    Assert.hasText(expressionSuffix, "Expression suffix must not be empty");
3✔
122
    this.expressionSuffix = expressionSuffix;
3✔
123
  }
1✔
124

125
  /**
126
   * Specify the EL parser to use for expression parsing.
127
   * <p>Default is a {@link SpelExpressionParser},
128
   * compatible with standard Unified EL style expression syntax.
129
   */
130
  public void setExpressionParser(ExpressionParser expressionParser) {
131
    Assert.notNull(expressionParser, "ExpressionParser is required");
×
132
    this.expressionParser = expressionParser;
×
133
  }
×
134

135
  @Override
136
  public boolean isTemplate() {
137
    return true;
2✔
138
  }
139

140
  @Override
141
  public String getExpressionPrefix() {
142
    return expressionPrefix;
3✔
143
  }
144

145
  @Override
146
  public String getExpressionSuffix() {
147
    return expressionSuffix;
3✔
148
  }
149

150
  @Override
151
  @Nullable
152
  public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException {
153
    if (StringUtils.isEmpty(value)) {
3✔
154
      return value;
2✔
155
    }
156
    try {
157
      Expression expr = expressionCache.get(value);
6✔
158
      if (expr == null) {
2✔
159
        expr = expressionParser.parseExpression(value, this);
6✔
160
        expressionCache.put(value, expr);
6✔
161
      }
162
      StandardEvaluationContext sec = evaluationCache.get(evalContext);
6✔
163
      if (sec == null) {
2✔
164
        sec = new StandardEvaluationContext(evalContext);
5✔
165
        sec.addPropertyAccessor(new BeanExpressionContextAccessor());
5✔
166
        sec.addPropertyAccessor(new BeanFactoryAccessor());
5✔
167
        sec.addPropertyAccessor(new MapAccessor());
5✔
168
        sec.addPropertyAccessor(new EnvironmentAccessor());
5✔
169
        sec.setBeanResolver(new BeanFactoryResolver(evalContext.beanFactory));
7✔
170
        sec.setTypeLocator(new StandardTypeLocator(evalContext.beanFactory.getBeanClassLoader()));
8✔
171
        sec.setTypeConverter(new StandardTypeConverter(() -> {
7✔
172
          ConversionService cs = evalContext.beanFactory.getConversionService();
4✔
173
          return cs != null ? cs : ApplicationConversionService.getSharedInstance();
6✔
174
        }));
175
        customizeEvaluationContext(sec);
3✔
176
        evaluationCache.put(evalContext, sec);
6✔
177
      }
178
      return expr.getValue(sec);
4✔
179
    }
180
    catch (Throwable ex) {
1✔
181
      throw new BeanExpressionException("Expression parsing failed", ex);
6✔
182
    }
183
  }
184

185
  /**
186
   * Template method for customizing the expression evaluation context.
187
   * <p>The default implementation is empty.
188
   */
189
  protected void customizeEvaluationContext(StandardEvaluationContext evalContext) {
190

191
  }
1✔
192

193
  private static int retrieveMaxExpressionLength() {
194
    String value = TodayStrategies.getProperty(MAX_SPEL_EXPRESSION_LENGTH_PROPERTY_NAME);
3✔
195
    if (StringUtils.isBlank(value)) {
3✔
196
      return SpelParserConfiguration.DEFAULT_MAX_EXPRESSION_LENGTH;
2✔
197
    }
198

199
    try {
200
      int maxLength = Integer.parseInt(value.trim());
4✔
201
      if (maxLength < 1) {
3✔
202
        throw new IllegalArgumentException("Value [%d] for system property [%s] must be positive"
8✔
203
                .formatted(maxLength, MAX_SPEL_EXPRESSION_LENGTH_PROPERTY_NAME));
9✔
204
      }
205
      return maxLength;
2✔
206
    }
207
    catch (NumberFormatException ex) {
1✔
208
      throw new IllegalArgumentException("Failed to parse value for system property [%s]: %s"
12✔
209
              .formatted(MAX_SPEL_EXPRESSION_LENGTH_PROPERTY_NAME, ex.getMessage()), ex);
6✔
210
    }
211
  }
212

213
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc