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

TAKETODAY / today-infrastructure / 18227001691

03 Oct 2025 03:44PM UTC coverage: 81.889% (+0.004%) from 81.885%
18227001691

push

github

TAKETODAY
:white_check_mark:

59822 of 78041 branches covered (76.65%)

Branch coverage included in aggregate %.

141269 of 167523 relevant lines covered (84.33%)

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.MapAccessor;
35
import infra.expression.spel.support.StandardEvaluationContext;
36
import infra.expression.spel.support.StandardTypeConverter;
37
import infra.expression.spel.support.StandardTypeLocator;
38
import infra.format.support.ApplicationConversionService;
39
import infra.lang.Assert;
40
import infra.lang.TodayStrategies;
41
import infra.util.StringUtils;
42

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

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

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

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

75
  private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX;
6✔
76

77
  private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
6✔
78

79
  private ExpressionParser expressionParser;
80

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

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

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

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

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

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

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

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

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

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

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

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

192
  }
1✔
193

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

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

214
}
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