001/* 002 * Copyright 2002-2018 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.util.Assert; 036import org.springframework.util.StringUtils; 037 038/** 039 * Standard implementation of the 040 * {@link org.springframework.beans.factory.config.BeanExpressionResolver} 041 * interface, parsing and evaluating Spring EL using Spring's expression module. 042 * 043 * @author Juergen Hoeller 044 * @since 3.0 045 * @see org.springframework.expression.ExpressionParser 046 * @see org.springframework.expression.spel.standard.SpelExpressionParser 047 * @see org.springframework.expression.spel.support.StandardEvaluationContext 048 */ 049public class StandardBeanExpressionResolver implements BeanExpressionResolver { 050 051 /** Default expression prefix: "#{" */ 052 public static final String DEFAULT_EXPRESSION_PREFIX = "#{"; 053 054 /** Default expression suffix: "}" */ 055 public static final String DEFAULT_EXPRESSION_SUFFIX = "}"; 056 057 058 private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX; 059 060 private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX; 061 062 private ExpressionParser expressionParser; 063 064 private final Map<String, Expression> expressionCache = new ConcurrentHashMap<String, Expression>(256); 065 066 private final Map<BeanExpressionContext, StandardEvaluationContext> evaluationCache = 067 new ConcurrentHashMap<BeanExpressionContext, StandardEvaluationContext>(8); 068 069 private final ParserContext beanExpressionParserContext = new ParserContext() { 070 @Override 071 public boolean isTemplate() { 072 return true; 073 } 074 @Override 075 public String getExpressionPrefix() { 076 return expressionPrefix; 077 } 078 @Override 079 public String getExpressionSuffix() { 080 return expressionSuffix; 081 } 082 }; 083 084 085 /** 086 * Create a new {@code StandardBeanExpressionResolver} with default settings. 087 */ 088 public StandardBeanExpressionResolver() { 089 this.expressionParser = new SpelExpressionParser(); 090 } 091 092 /** 093 * Create a new {@code StandardBeanExpressionResolver} with the given bean class loader, 094 * using it as the basis for expression compilation. 095 * @param beanClassLoader the factory's bean class loader 096 */ 097 public StandardBeanExpressionResolver(ClassLoader beanClassLoader) { 098 this.expressionParser = new SpelExpressionParser(new SpelParserConfiguration(null, beanClassLoader)); 099 } 100 101 102 /** 103 * Set the prefix that an expression string starts with. 104 * The default is "#{". 105 * @see #DEFAULT_EXPRESSION_PREFIX 106 */ 107 public void setExpressionPrefix(String expressionPrefix) { 108 Assert.hasText(expressionPrefix, "Expression prefix must not be empty"); 109 this.expressionPrefix = expressionPrefix; 110 } 111 112 /** 113 * Set the suffix that an expression string ends with. 114 * The default is "}". 115 * @see #DEFAULT_EXPRESSION_SUFFIX 116 */ 117 public void setExpressionSuffix(String expressionSuffix) { 118 Assert.hasText(expressionSuffix, "Expression suffix must not be empty"); 119 this.expressionSuffix = expressionSuffix; 120 } 121 122 /** 123 * Specify the EL parser to use for expression parsing. 124 * <p>Default is a {@link org.springframework.expression.spel.standard.SpelExpressionParser}, 125 * compatible with standard Unified EL style expression syntax. 126 */ 127 public void setExpressionParser(ExpressionParser expressionParser) { 128 Assert.notNull(expressionParser, "ExpressionParser must not be null"); 129 this.expressionParser = expressionParser; 130 } 131 132 133 @Override 134 public Object evaluate(String value, BeanExpressionContext evalContext) throws BeansException { 135 if (!StringUtils.hasLength(value)) { 136 return value; 137 } 138 try { 139 Expression expr = this.expressionCache.get(value); 140 if (expr == null) { 141 expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext); 142 this.expressionCache.put(value, expr); 143 } 144 StandardEvaluationContext sec = this.evaluationCache.get(evalContext); 145 if (sec == null) { 146 sec = new StandardEvaluationContext(evalContext); 147 sec.addPropertyAccessor(new BeanExpressionContextAccessor()); 148 sec.addPropertyAccessor(new BeanFactoryAccessor()); 149 sec.addPropertyAccessor(new MapAccessor()); 150 sec.addPropertyAccessor(new EnvironmentAccessor()); 151 sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory())); 152 sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader())); 153 ConversionService conversionService = evalContext.getBeanFactory().getConversionService(); 154 if (conversionService != null) { 155 sec.setTypeConverter(new StandardTypeConverter(conversionService)); 156 } 157 customizeEvaluationContext(sec); 158 this.evaluationCache.put(evalContext, sec); 159 } 160 return expr.getValue(sec); 161 } 162 catch (Throwable ex) { 163 throw new BeanExpressionException("Expression parsing failed", ex); 164 } 165 } 166 167 /** 168 * Template method for customizing the expression evaluation context. 169 * <p>The default implementation is empty. 170 */ 171 protected void customizeEvaluationContext(StandardEvaluationContext evalContext) { 172 } 173 174}