spring SimpleEvaluationContext 源码

  • 2022-08-08
  • 浏览 (270)

spring SimpleEvaluationContext 代码

文件路径:/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.expression.spel.support;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.OperatorOverloader;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;

/**
 * A basic implementation of {@link EvaluationContext} that focuses on a subset
 * of essential SpEL features and customization options, targeting simple
 * condition evaluation and in particular data binding scenarios.
 *
 * <p>In many cases, the full extent of the SpEL language is not required and
 * should be meaningfully restricted. Examples include but are not limited to
 * data binding expressions, property-based filters, and others. To that effect,
 * {@code SimpleEvaluationContext} is tailored to support only a subset of the
 * SpEL language syntax, e.g. excluding references to Java types, constructors,
 * and bean references.
 *
 * <p>When creating a {@code SimpleEvaluationContext} you need to choose the
 * level of support that you need for property access in SpEL expressions:
 * <ul>
 * <li>A custom {@code PropertyAccessor} (typically not reflection-based),
 * potentially combined with a {@link DataBindingPropertyAccessor}</li>
 * <li>Data binding properties for read-only access</li>
 * <li>Data binding properties for read and write</li>
 * </ul>
 *
 * <p>Conveniently, {@link SimpleEvaluationContext#forReadOnlyDataBinding()}
 * enables read access to properties via {@link DataBindingPropertyAccessor};
 * same for {@link SimpleEvaluationContext#forReadWriteDataBinding()} when
 * write access is needed as well. Alternatively, configure custom accessors
 * via {@link SimpleEvaluationContext#forPropertyAccessors}, and potentially
 * activate method resolution and/or a type converter through the builder.
 *
 * <p>Note that {@code SimpleEvaluationContext} is typically not configured
 * with a default root object. Instead it is meant to be created once and
 * used repeatedly through {@code getValue} calls on a pre-compiled
 * {@link org.springframework.expression.Expression} with both an
 * {@code EvaluationContext} and a root object as arguments:
 * {@link org.springframework.expression.Expression#getValue(EvaluationContext, Object)}.
 *
 * <p>For more power and flexibility, in particular for internal configuration
 * scenarios, consider using {@link StandardEvaluationContext} instead.
 *
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @since 4.3.15
 * @see #forPropertyAccessors
 * @see #forReadOnlyDataBinding()
 * @see #forReadWriteDataBinding()
 * @see StandardEvaluationContext
 * @see StandardTypeConverter
 * @see DataBindingPropertyAccessor
 */
public final class SimpleEvaluationContext implements EvaluationContext {

	private static final TypeLocator typeNotFoundTypeLocator = typeName -> {
		throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName);
	};


	private final TypedValue rootObject;

	private final List<PropertyAccessor> propertyAccessors;

	private final List<MethodResolver> methodResolvers;

	private final TypeConverter typeConverter;

	private final TypeComparator typeComparator = new StandardTypeComparator();

	private final OperatorOverloader operatorOverloader = new StandardOperatorOverloader();

	private final Map<String, Object> variables = new HashMap<>();


	private SimpleEvaluationContext(List<PropertyAccessor> accessors, List<MethodResolver> resolvers,
			@Nullable TypeConverter converter, @Nullable TypedValue rootObject) {

		this.propertyAccessors = accessors;
		this.methodResolvers = resolvers;
		this.typeConverter = (converter != null ? converter : new StandardTypeConverter());
		this.rootObject = (rootObject != null ? rootObject : TypedValue.NULL);
	}


	/**
	 * Return the specified root object, if any.
	 */
	@Override
	public TypedValue getRootObject() {
		return this.rootObject;
	}

	/**
	 * Return the specified {@link PropertyAccessor} delegates, if any.
	 * @see #forPropertyAccessors
	 */
	@Override
	public List<PropertyAccessor> getPropertyAccessors() {
		return this.propertyAccessors;
	}

	/**
	 * Return an empty list, always, since this context does not support the
	 * use of type references.
	 */
	@Override
	public List<ConstructorResolver> getConstructorResolvers() {
		return Collections.emptyList();
	}

	/**
	 * Return the specified {@link MethodResolver} delegates, if any.
	 * @see Builder#withMethodResolvers
	 */
	@Override
	public List<MethodResolver> getMethodResolvers() {
		return this.methodResolvers;
	}

	/**
	 * {@code SimpleEvaluationContext} does not support the use of bean references.
	 * @return always {@code null}
	 */
	@Override
	@Nullable
	public BeanResolver getBeanResolver() {
		return null;
	}

	/**
	 * {@code SimpleEvaluationContext} does not support use of type references.
	 * @return {@code TypeLocator} implementation that raises a
	 * {@link SpelEvaluationException} with {@link SpelMessage#TYPE_NOT_FOUND}.
	 */
	@Override
	public TypeLocator getTypeLocator() {
		return typeNotFoundTypeLocator;
	}

	/**
	 * The configured {@link TypeConverter}.
	 * <p>By default this is {@link StandardTypeConverter}.
	 * @see Builder#withTypeConverter
	 * @see Builder#withConversionService
	 */
	@Override
	public TypeConverter getTypeConverter() {
		return this.typeConverter;
	}

	/**
	 * Return an instance of {@link StandardTypeComparator}.
	 */
	@Override
	public TypeComparator getTypeComparator() {
		return this.typeComparator;
	}

	/**
	 * Return an instance of {@link StandardOperatorOverloader}.
	 */
	@Override
	public OperatorOverloader getOperatorOverloader() {
		return this.operatorOverloader;
	}

	@Override
	public void setVariable(String name, @Nullable Object value) {
		this.variables.put(name, value);
	}

	@Override
	@Nullable
	public Object lookupVariable(String name) {
		return this.variables.get(name);
	}


	/**
	 * Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor}
	 * delegates: typically a custom {@code PropertyAccessor} specific to a use case
	 * (e.g. attribute resolution in a custom data structure), potentially combined with
	 * a {@link DataBindingPropertyAccessor} if property dereferences are needed as well.
	 * @param accessors the accessor delegates to use
	 * @see DataBindingPropertyAccessor#forReadOnlyAccess()
	 * @see DataBindingPropertyAccessor#forReadWriteAccess()
	 */
	public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
		for (PropertyAccessor accessor : accessors) {
			if (accessor.getClass() == ReflectivePropertyAccessor.class) {
				throw new IllegalArgumentException("SimpleEvaluationContext is not designed for use with a plain " +
						"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass.");
			}
		}
		return new Builder(accessors);
	}

	/**
	 * Create a {@code SimpleEvaluationContext} for read-only access to
	 * public properties via {@link DataBindingPropertyAccessor}.
	 * @see DataBindingPropertyAccessor#forReadOnlyAccess()
	 * @see #forPropertyAccessors
	 */
	public static Builder forReadOnlyDataBinding() {
		return new Builder(DataBindingPropertyAccessor.forReadOnlyAccess());
	}

	/**
	 * Create a {@code SimpleEvaluationContext} for read-write access to
	 * public properties via {@link DataBindingPropertyAccessor}.
	 * @see DataBindingPropertyAccessor#forReadWriteAccess()
	 * @see #forPropertyAccessors
	 */
	public static Builder forReadWriteDataBinding() {
		return new Builder(DataBindingPropertyAccessor.forReadWriteAccess());
	}


	/**
	 * Builder for {@code SimpleEvaluationContext}.
	 */
	public static class Builder {

		private final List<PropertyAccessor> accessors;

		private List<MethodResolver> resolvers = Collections.emptyList();

		@Nullable
		private TypeConverter typeConverter;

		@Nullable
		private TypedValue rootObject;

		public Builder(PropertyAccessor... accessors) {
			this.accessors = Arrays.asList(accessors);
		}

		/**
		 * Register the specified {@link MethodResolver} delegates for
		 * a combination of property access and method resolution.
		 * @param resolvers the resolver delegates to use
		 * @see #withInstanceMethods()
		 * @see SimpleEvaluationContext#forPropertyAccessors
		 */
		public Builder withMethodResolvers(MethodResolver... resolvers) {
			for (MethodResolver resolver : resolvers) {
				if (resolver.getClass() == ReflectiveMethodResolver.class) {
					throw new IllegalArgumentException("SimpleEvaluationContext is not designed for use with a plain " +
							"ReflectiveMethodResolver. Consider using DataBindingMethodResolver or a custom subclass.");
				}
			}
			this.resolvers = Arrays.asList(resolvers);
			return this;
		}

		/**
		 * Register a {@link DataBindingMethodResolver} for instance method invocation purposes
		 * (i.e. not supporting static methods) in addition to the specified property accessors,
		 * typically in combination with a {@link DataBindingPropertyAccessor}.
		 * @see #withMethodResolvers
		 * @see SimpleEvaluationContext#forReadOnlyDataBinding()
		 * @see SimpleEvaluationContext#forReadWriteDataBinding()
		 */
		public Builder withInstanceMethods() {
			this.resolvers = Collections.singletonList(DataBindingMethodResolver.forInstanceMethodInvocation());
			return this;
		}


		/**
		 * Register a custom {@link ConversionService}.
		 * <p>By default a {@link StandardTypeConverter} backed by a
		 * {@link org.springframework.core.convert.support.DefaultConversionService} is used.
		 * @see #withTypeConverter
		 * @see StandardTypeConverter#StandardTypeConverter(ConversionService)
		 */
		public Builder withConversionService(ConversionService conversionService) {
			this.typeConverter = new StandardTypeConverter(conversionService);
			return this;
		}
		/**
		 * Register a custom {@link TypeConverter}.
		 * <p>By default a {@link StandardTypeConverter} backed by a
		 * {@link org.springframework.core.convert.support.DefaultConversionService} is used.
		 * @see #withConversionService
		 * @see StandardTypeConverter#StandardTypeConverter()
		 */
		public Builder withTypeConverter(TypeConverter converter) {
			this.typeConverter = converter;
			return this;
		}

		/**
		 * Specify a default root object to resolve against.
		 * <p>Default is none, expecting an object argument at evaluation time.
		 * @see org.springframework.expression.Expression#getValue(EvaluationContext)
		 * @see org.springframework.expression.Expression#getValue(EvaluationContext, Object)
		 */
		public Builder withRootObject(Object rootObject) {
			this.rootObject = new TypedValue(rootObject);
			return this;
		}

		/**
		 * Specify a typed root object to resolve against.
		 * <p>Default is none, expecting an object argument at evaluation time.
		 * @see org.springframework.expression.Expression#getValue(EvaluationContext)
		 * @see org.springframework.expression.Expression#getValue(EvaluationContext, Object)
		 */
		public Builder withTypedRootObject(Object rootObject, TypeDescriptor typeDescriptor) {
			this.rootObject = new TypedValue(rootObject, typeDescriptor);
			return this;
		}

		public SimpleEvaluationContext build() {
			return new SimpleEvaluationContext(this.accessors, this.resolvers, this.typeConverter, this.rootObject);
		}
	}

}

相关信息

spring 源码目录

相关文章

spring BooleanTypedValue 源码

spring DataBindingMethodResolver 源码

spring DataBindingPropertyAccessor 源码

spring ReflectionHelper 源码

spring ReflectiveConstructorExecutor 源码

spring ReflectiveConstructorResolver 源码

spring ReflectiveMethodExecutor 源码

spring ReflectiveMethodResolver 源码

spring ReflectivePropertyAccessor 源码

spring StandardEvaluationContext 源码

0  赞