001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.expression; 018 019import java.util.Map; 020import java.util.concurrent.ConcurrentHashMap; 021 022import org.springframework.beans.BeansException; 023import org.springframework.beans.factory.BeanExpressionException; 024import org.springframework.beans.factory.config.BeanExpressionContext; 025import org.springframework.beans.factory.config.BeanExpressionResolver; 026import org.springframework.core.convert.ConversionService; 027import org.springframework.expression.Expression; 028import org.springframework.expression.ExpressionParser; 029import org.springframework.expression.ParserContext; 030import org.springframework.expression.spel.SpelParserConfiguration; 031import org.springframework.expression.spel.standard.SpelExpressionParser; 032import org.springframework.expression.spel.support.StandardEvaluationContext; 033import org.springframework.expression.spel.support.StandardTypeConverter; 034import org.springframework.expression.spel.support.StandardTypeLocator; 035import org.springframework.lang.Nullable; 036import org.springframework.util.Assert; 037import org.springframework.util.StringUtils; 038 039/** 040 * Standard implementation of the 041 * {@link org.springframework.beans.factory.config.BeanExpressionResolver} 042 * interface, parsing and evaluating Spring EL using Spring's expression module. 043 * 044 * <p>All beans in the containing {@code BeanFactory} are made available as 045 * predefined variables with their common bean name, including standard context 046 * beans such as "environment", "systemProperties" and "systemEnvironment". 047 * 048 * @author Juergen Hoeller 049 * @since 3.0 050 * @see BeanExpressionContext#getBeanFactory() 051 * @see org.springframework.expression.ExpressionParser 052 * @see org.springframework.expression.spel.standard.SpelExpressionParser 053 * @see org.springframework.expression.spel.support.StandardEvaluationContext 054 */ 055public class StandardBeanExpressionResolver implements BeanExpressionResolver { 056 057 /** Default expression prefix: "#{". */ 058 public static final String DEFAULT_EXPRESSION_PREFIX = "#{"; 059 060 /** Default expression suffix: "}". */ 061 public static final String DEFAULT_EXPRESSION_SUFFIX = "}"; 062 063 064 private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX; 065 066 private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX; 067 068 private ExpressionParser expressionParser; 069 070 private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>(256); 071 072 private final Map<BeanExpressionContext, StandardEvaluationContext> evaluationCache = new ConcurrentHashMap<>(8); 073 074 private final ParserContext beanExpressionParserContext = new ParserContext() { 075 @Override 076 public boolean isTemplate() { 077 return true; 078 } 079 @Override 080 public String getExpressionPrefix() { 081 return expressionPrefix; 082 } 083 @Override 084 public String getExpressionSuffix() { 085 return expressionSuffix; 086 } 087 }; 088 089 090 /** 091 * Create a new {@code StandardBeanExpressionResolver} with default settings. 092 */ 093 public StandardBeanExpressionResolver() { 094 this.expressionParser = new SpelExpressionParser(); 095 } 096 097 /** 098 * Create a new {@code StandardBeanExpressionResolver} with the given bean class loader, 099 * using it as the basis for expression compilation. 100 * @param beanClassLoader the factory's bean class loader 101 */ 102 public StandardBeanExpressionResolver(@Nullable ClassLoader beanClassLoader) { 103 this.expressionParser = new SpelExpressionParser(new SpelParserConfiguration(null, beanClassLoader)); 104 } 105 106 107 /** 108 * Set the prefix that an expression string starts with. 109 * The default is "#{". 110 * @see #DEFAULT_EXPRESSION_PREFIX 111 */ 112 public void setExpressionPrefix(String expressionPrefix) { 113 Assert.hasText(expressionPrefix, "Expression prefix must not be empty"); 114 this.expressionPrefix = expressionPrefix; 115 } 116 117 /** 118 * Set the suffix that an expression string ends with. 119 * The default is "}". 120 * @see #DEFAULT_EXPRESSION_SUFFIX 121 */ 122 public void setExpressionSuffix(String expressionSuffix) { 123 Assert.hasText(expressionSuffix, "Expression suffix must not be empty"); 124 this.expressionSuffix = expressionSuffix; 125 } 126 127 /** 128 * Specify the EL parser to use for expression parsing. 129 * <p>Default is a {@link org.springframework.expression.spel.standard.SpelExpressionParser}, 130 * compatible with standard Unified EL style expression syntax. 131 */ 132 public void setExpressionParser(ExpressionParser expressionParser) { 133 Assert.notNull(expressionParser, "ExpressionParser must not be null"); 134 this.expressionParser = expressionParser; 135 } 136 137 138 @Override 139 @Nullable 140 public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException { 141 if (!StringUtils.hasLength(value)) { 142 return value; 143 } 144 try { 145 Expression expr = this.expressionCache.get(value); 146 if (expr == null) { 147 expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext); 148 this.expressionCache.put(value, expr); 149 } 150 StandardEvaluationContext sec = this.evaluationCache.get(evalContext); 151 if (sec == null) { 152 sec = new StandardEvaluationContext(evalContext); 153 sec.addPropertyAccessor(new BeanExpressionContextAccessor()); 154 sec.addPropertyAccessor(new BeanFactoryAccessor()); 155 sec.addPropertyAccessor(new MapAccessor()); 156 sec.addPropertyAccessor(new EnvironmentAccessor()); 157 sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory())); 158 sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader())); 159 ConversionService conversionService = evalContext.getBeanFactory().getConversionService(); 160 if (conversionService != null) { 161 sec.setTypeConverter(new StandardTypeConverter(conversionService)); 162 } 163 customizeEvaluationContext(sec); 164 this.evaluationCache.put(evalContext, sec); 165 } 166 return expr.getValue(sec); 167 } 168 catch (Throwable ex) { 169 throw new BeanExpressionException("Expression parsing failed", ex); 170 } 171 } 172 173 /** 174 * Template method for customizing the expression evaluation context. 175 * <p>The default implementation is empty. 176 */ 177 protected void customizeEvaluationContext(StandardEvaluationContext evalContext) { 178 } 179 180}