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}