依赖注入之@Value原理(SpEL表达式解析)

evaluateBeanDefinitionString是doResolveDependency方法处理@Value注解的第三步,evaluateBeanDefinitionString解析@Value注解中设置value的SpEL表达式,将#{xxx}替换为BeanExpressionResolver解析出来的值。
回顾一下doResolveDependency方法,如下

	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {

			Class<?> type = descriptor.getDependencyType();
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			if (value != null) {
				if (value instanceof String) {
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ?
							getMergedBeanDefinition(beanName) : null);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					// A custom TypeConverter which does not support TypeDescriptor resolution...
					return (descriptor.getField() != null ?
							converter.convertIfNecessary(value, type, descriptor.getField()) :
							converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
				}
			}
            ...
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

前面分析过,evaluateBeanDefinitionString将会调用到StandardBeanExpressionResolver#evaluate方法,从这个方法开始分析

	public Object evaluate(@Nullable String value, BeanExpressionContext beanExpressionContext) throws BeansException {
		if (!StringUtils.hasLength(value)) {
			return value;
		}
		try {
			Expression expr = this.expressionCache.get(value);
			if (expr == null) {
				expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext);
				this.expressionCache.put(value, expr);
			}
			StandardEvaluationContext sec = this.evaluationCache.get(beanExpressionContext);
			if (sec == null) {
				sec = new StandardEvaluationContext(beanExpressionContext);
				sec.addPropertyAccessor(new BeanExpressionContextAccessor());
				sec.addPropertyAccessor(new BeanFactoryAccessor());
				sec.addPropertyAccessor(new MapAccessor());
				sec.addPropertyAccessor(new EnvironmentAccessor());
				sec.setBeanResolver(new BeanFactoryResolver(beanExpressionContext.getBeanFactory()));
				sec.setTypeLocator(new StandardTypeLocator(beanExpressionContext.getBeanFactory().getBeanClassLoader()));
				sec.setTypeConverter(new StandardTypeConverter(() -> {
					ConversionService cs = beanExpressionContext.getBeanFactory().getConversionService();
					return (cs != null ? cs : DefaultConversionService.getSharedInstance());
				}));
				customizeEvaluationContext(sec);
				this.evaluationCache.put(beanExpressionContext, sec);
			}
			return expr.getValue(sec);
		}
		catch (Throwable ex) {
			throw new BeanExpressionException("Expression parsing failed", ex);
		}
	}

StandardBeanExpressionResolver#evaluate做了三件事情,

  • 调用expressionParser解析出Expression
  • 实例化了一个StandardEvaluationContext
  • 返回Expression#getValue的结果

StandardEvaluationContext包装了与Spring交互的工具,不深入分析。看另外两个

  • Expression解析

parseExpression方法的第二个参数beanExpressionParserContext就是一个匿名内部类实例化的对象,就是定义了模板的prefix和suffix,分别是#{和},isTemplate返回true

	private final ParserContext beanExpressionParserContext = new ParserContext() {
		@Override
		public boolean isTemplate() {
			return true;
		}
		@Override
		public String getExpressionPrefix() {
			return expressionPrefix;
		}
		@Override
		public String getExpressionSuffix() {
			return expressionSuffix;
		}
	};

parseExpression方法会调用到TemplateAwareExpressionParser#parseExpression方法,会进而调用parseTemplate方法

	public Expression parseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {
		if (context != null && context.isTemplate()) {
			return parseTemplate(expressionString, context);
		}
		else {
			return doParseExpression(expressionString, context);
		}
	}

parseTemplate逻辑也比较简单,调用了parseExpressions

	private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException {
		if (expressionString.isEmpty()) {
			return new LiteralExpression("");
		}

		Expression[] expressions = parseExpressions(expressionString, context);
		if (expressions.length == 1) {
			return expressions[0];
		}
		else {
			return new CompositeStringExpression(expressionString, expressions);
		}
	}

parseExpressions即将expressionString按prefix和suffix分割,对#{、}里面的内容调用doParseExpression方法解析成SpEL的语法树,不在深入分析。

	private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException {
		List<Expression> expressions = new ArrayList<>();
		String prefix = context.getExpressionPrefix();
		String suffix = context.getExpressionSuffix();
		int startIdx = 0;

		while (startIdx < expressionString.length()) {
			int prefixIndex = expressionString.indexOf(prefix, startIdx);
			if (prefixIndex >= startIdx) {
				// an inner expression was found - this is a composite
				if (prefixIndex > startIdx) {
					expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
				}
				int afterPrefixIndex = prefixIndex + prefix.length();
				int suffixIndex = skipToCorrectEndSuffix(suffix, expressionString, afterPrefixIndex);
				if (suffixIndex == -1) {
					throw new ParseException(expressionString, prefixIndex,
							"No ending suffix '" + suffix + "' for expression starting at character " +
							prefixIndex + ": " + expressionString.substring(prefixIndex));
				}
				if (suffixIndex == afterPrefixIndex) {
					throw new ParseException(expressionString, prefixIndex,
							"No expression defined within delimiter '" + prefix + suffix +
							"' at character " + prefixIndex);
				}
				String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
				expr = expr.trim();
				if (expr.isEmpty()) {
					throw new ParseException(expressionString, prefixIndex,
							"No expression defined within delimiter '" + prefix + suffix +
							"' at character " + prefixIndex);
				}
				expressions.add(doParseExpression(expr, context));
				startIdx = suffixIndex + suffix.length();
			}
			else {
				// no more ${expressions} found in string, add rest as static text
				expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
				startIdx = expressionString.length();
			}
		}

		return expressions.toArray(new Expression[0]);
	}
  • Expression#getValue

Expression有三个实现。LiteralExpression、SpelExpression、CompositeStringExpression。LiteralExpression和CompositeStringExpression的逻辑都很简单。
LiteralExpression就是字面量表达式,getValue方法直接返回这个字面量

	public String getValue(@Nullable Object rootObject) {
		return this.literalValue;
	}

CompositeStringExpression组合多个Expression,getValue方法遍历所有Expression#getValue,拼接返回

	public String getValue() throws EvaluationException {
		StringBuilder sb = new StringBuilder();
		for (Expression expression : this.expressions) {
			String value = expression.getValue(String.class);
			if (value != null) {
				sb.append(value);
			}
		}
		return sb.toString();
	}

SpelExpression实现比较复杂,核心逻辑外就是调用了ast.getTypedValue方法

	public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
		CompiledExpression compiledAst = this.compiledAst;
		if (compiledAst != null) {
			try {
				EvaluationContext context = getEvaluationContext();
				Object result = compiledAst.getValue(context.getRootObject().getValue(), context);
				if (expectedResultType == null) {
					return (T) result;
				}
				else {
					return ExpressionUtils.convertTypedValue(
							getEvaluationContext(), new TypedValue(result), expectedResultType);
				}
			}
			catch (Throwable ex) {
				// If running in mixed mode, revert to interpreted
				if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
					this.compiledAst = null;
					this.interpretedCount.set(0);
				}
				else {
					// Running in SpelCompilerMode.immediate mode - propagate exception to caller
					throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
				}
			}
		}

		ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
		TypedValue typedResultValue = this.ast.getTypedValue(expressionState);
		checkCompile(expressionState);
		return ExpressionUtils.convertTypedValue(
				expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
	}

ast是前面解析SpelExpression生成的语法树,语法树的每个节点都是SpelNodeImpl。SpelNodeImpl代表SpEL支持的各种语法,有很多子类,例如:

  • 字面量,IntLiteral、LongLiteral、NullLiteral等
  • 比较操作,OpEQ、OpNE等
  • 从beanFactory获取bean,BeanReference
  • 数组、List、Map按索引取元素,Indexer

SpelNodeImpl一个比较重要的属性就是children,就是当前节点的孩子。

public abstract class SpelNodeImpl implements SpelNode, Opcodes {


	protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
}

例如表达式1 == 2解析成的语法树根节点为OpEQ,根节点有两个IntLiteral子节点,分别是1和2。
ast#getTypedValue进一步调用getValueInternal方法,getValueInternal在SpelNodeImpl各个子类去实现。看几个SpelNodeImpl的实现
1、OpEQ
OpEQ有两个children,分别对左Operand和右Operand求值,判断相等

	public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
		Object left = getLeftOperand().getValueInternal(state).getValue();
		Object right = getRightOperand().getValueInternal(state).getValue();
		this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
		this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
		return BooleanTypedValue.forValue(equalityCheck(state.getEvaluationContext(), left, right));
	}

2、BeanReference
获取到BeanResolver,通过beanResolver#resolve按name获取bean

	public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
		BeanResolver beanResolver = state.getEvaluationContext().getBeanResolver();
		if (beanResolver == null) {
			throw new SpelEvaluationException(
					getStartPosition(), SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanName);
		}

		try {
			return new TypedValue(beanResolver.resolve(state.getEvaluationContext(), this.beanName));
		}
		catch (AccessException ex) {
			throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
				this.beanName, ex.getMessage());
		}
	}

beanResolver就是BeanFactoryResolver,从beanFactory获取bean

	public Object resolve(EvaluationContext context, String beanName) throws AccessException {
		try {
			return this.beanFactory.getBean(beanName);
		}
		catch (BeansException ex) {
			throw new AccessException("Could not resolve bean reference against BeanFactory", ex);
		}
	}

所有支持的语法都在org.springframework.expression.spel.ast包下

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值