Spring源码:Spring EL表达式源码分析

目录

1.EL表达式处理过程

1.1 根据EL表达式字符串构建抽象语法树(ast)

1.2 构造标准评估上下文对象StandardEvaluationContext

1.3 利用标准评估上下文对象StandardEvaluationContext解析EL表达式语法树

2.Spring框架中如何使用到EL表达式

3.应用EL表达式的条件注解@OnExpressionCondition


1.EL表达式处理过程

首先看一下EL表达式在代码中的使用方式:

BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null);
Object result = new StandardBeanExpressionResolver().evaluate(expression, expressionContext);

BeanExpressionContext:表示bean表达式上下文对象,用于获取bean对象,这里表示从beanFactory工厂中获取指定bean名称的bean对象;

StandardBeanExpressionResolver:表示标准的bean表达式解析器,实现接口BeanExpressionResolver;对EL表达式字符串,在指定的bean表达式上下文中进行解析;

下面主要看一下BeanExpressionResolver的接口方法evaluate在StandardBeanExpressionResolver的实现逻辑:

  /**
	 * Evaluate the given value as an expression, if applicable;
	 * return the value as-is otherwise.
	 * @param value the value to check
	 * @param evalContext the evaluation context
	 * @return the resolved value (potentially the given value as-is)
	 * @throws BeansException if evaluation failed
	 */
@Override
@Nullable
public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) 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(evalContext);
			if (sec == null) {
				sec = new StandardEvaluationContext(evalContext);
				sec.addPropertyAccessor(new BeanExpressionContextAccessor());
				sec.addPropertyAccessor(new BeanFactoryAccessor());
				sec.addPropertyAccessor(new MapAccessor());
				sec.addPropertyAccessor(new EnvironmentAccessor());
				sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory()));
				sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader()));
				ConversionService conversionService = evalContext.getBeanFactory().getConversionService();
				if (conversionService != null) {
					sec.setTypeConverter(new StandardTypeConverter(conversionService));
				}
				customizeEvaluationContext(sec);
				this.evaluationCache.put(evalContext, sec);
			}
			return expr.getValue(sec);
		}
		catch (Throwable ex) {
			throw new BeanExpressionException("Expression parsing failed", ex);
		}
}

这里主要包含以下几步:

1.1 根据EL表达式字符串构建抽象语法树(ast)

1.2 构造标准评估上下文对象StandardEvaluationContext

1.3 利用标准评估上下文对象StandardEvaluationContext解析EL表达式语法树,得出计算结果

下面分别进行说明:

1.1 根据EL表达式字符串构建抽象语法树(ast)

构建抽象语法树的关键代码如下:InternalSpelExpressionParser

@Override
	protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
		try {
			this.expressionString = expressionString;
			Tokenizer tokenizer = new Tokenizer(expressionString);
			tokenizer.process();
			this.tokenStream = tokenizer.getTokens();
			this.tokenStreamLength = this.tokenStream.size();
			this.tokenStreamPointer = 0;
			this.constructedNodes.clear();
			SpelNodeImpl ast = eatExpression();
			if (moreTokens()) {
				throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
			}
			Assert.isTrue(this.constructedNodes.isEmpty());
			return new SpelExpression(expressionString, ast, this.configuration);
		}
		catch (InternalParseException ex) {
			throw ex.getCause();
		}
	}

这里也主要分为2步:

1.根据字符串表达式识别出token流,简单来说就是一个分词的过程;

2.根据构建的token流构建抽象语法树,这里按照EL表达式支持的运算符的优先级进行解析,构建了一个树形结构,每个节点都表示一个运算单元(操作符和若干操作数),比如OpPlus(加法运算),OpMinus(减法运算);

最终的构造的语法数的根节点表示整个表达式的优先级最低的运算单元,比如:1+(2-3),根节点即是:OpPlus,加法运算,两个操作数为1 和(2-3);

1.2 构造标准评估上下文对象StandardEvaluationContext

StandardEvaluationContext对象主要包含了一下几部分功能:

  • beanFactory(用于获取指定bean)

  • environment用于获取指定属性

  • map用于获取map结构的值

  • ...

  • 并且提供了可扩展的接口方法customizeEvaluationContext

1.3 利用标准评估上下文对象StandardEvaluationContext解析EL表达式语法树

这里利用标准评估上下文对象StandardEvaluationContext来对抽象语法树进行解析,实际是一个深度优先搜索的计算过程,最终返回整个表达式的计算结果;

@Override
	@Nullable
	public Object getValue(EvaluationContext context) throws EvaluationException {
		Assert.notNull(context, "EvaluationContext is required");

		ExpressionState expressionState = new ExpressionState(context, this.configuration);
		Object result = this.ast.getValue(expressionState);
		checkCompile(expressionState);
		return result;
	}

2.Spring框架中如何使用到EL表达式

Spring在启动时,会配置好SpEl表达式引擎,具体过程是在AbstractApplicationContext的refresh方法当中会对beanFactory进行配置,如下:

/**
	 * Configure the factory's standard context characteristics,
	 * such as the context's ClassLoader and post-processors.
	 * @param beanFactory the BeanFactory to configure
	 */
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader etc.
		beanFactory.setBeanClassLoader(getClassLoader());
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// Configure the bean factory with context callbacks.
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// Detect a LoadTimeWeaver and prepare for weaving, if found.
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}

		// Register default environment beans.
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}

3.应用EL表达式的条件注解@OnExpressionCondition

@OnExpressionCondition 是Spring提供的根据表达式计算结果来有条件加载bean的一种方式;

/**
 * Configuration annotation for a conditional element that depends on the value of a SpEL
 * expression.
 *
 * @author Dave Syer
 * @since 1.0.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {

	/**
	 * The SpEL expression to evaluate. Expression should return {@code true} if the
	 * condition passes or {@code false} if it fails.
	 * @return the SpEL expression
	 */
	String value() default "true";
}

主要实现代码如下,包括占位符解析,SpEL表达式计算;

/**
 * A Condition that evaluates a SpEL expression.
 *
 * @author Dave Syer
 * @author Stephane Nicoll
 * @see ConditionalOnExpression
 */
@Order(Ordered.LOWEST_PRECEDENCE - 20)
class OnExpressionCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		String expression = (String) metadata.getAnnotationAttributes(ConditionalOnExpression.class.getName())
				.get("value");
		expression = wrapIfNecessary(expression);
		ConditionMessage.Builder messageBuilder = ConditionMessage.forCondition(ConditionalOnExpression.class,
				"(" + expression + ")");
		expression = context.getEnvironment().resolvePlaceholders(expression);
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		if (beanFactory != null) {
			boolean result = evaluateExpression(beanFactory, expression);
			return new ConditionOutcome(result, messageBuilder.resultedIn(result));
		}
		return ConditionOutcome.noMatch(messageBuilder.because("no BeanFactory available."));
	}

	private Boolean evaluateExpression(ConfigurableListableBeanFactory beanFactory, String expression) {
		BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
		if (resolver == null) {
			resolver = new StandardBeanExpressionResolver();
		}
		BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null);
		Object result = resolver.evaluate(expression, expressionContext);
		return (result != null && (boolean) result);
	}

	/**
	 * Allow user to provide bare expression with no '#{}' wrapper.
	 * @param expression source expression
	 * @return wrapped expression
	 */
	private String wrapIfNecessary(String expression) {
		if (!expression.startsWith("#{")) {
			return "#{" + expression + "}";
		}
		return expression;
	}

}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EL表达式 (详解) EL表达式 1、EL简介 1)语法结构 ${expression} 2)[]与.运算符 EL 提供.和[]两种运算符来存取数据。 当要存取的属性名称中包含一些特殊字符,如.或?等并非字母或数字的符号,就一定要使用 []。 例如: ${user.My-Name}应当改为${user["My-Name"] } 如果要动态取值时,就可以用[]来做,而.无法做到动态取值。例如: ${sessionScope.user[data]}中data 是一个变量 3)变量 EL存取变量数据的方法很简单,例如:${username}。它的意思是取出某一范围中名称为 username的变量。 因为我们并没有指定哪一个范围的username,所以它会依序从Page、Request、Session、 Application范围查找。 假如途中找到username,就直接回传,不再继续找下去,但是假如全部的范围都没有找到时, 就回传null。 属性范围在EL中的名称 Page PageScope Request RequestScope Session SessionScope Application ApplicationScope 4) 1--EL表达式用${}表示,可用在所有的HTML和JSP标签中作用是代替JSP页面中复杂的JAVA代码. 2--EL表达式可操作常量 变量 和隐式对象. 最常用的 隐式对象有${param}和${paramValues}. ${param}表示返回请求参数中单个字符串的值. ${paramValues}表示返回请求参数的一组 值.pageScope表示页面范围的变量.requestScope表示请求对象的变量. sessionScope表示会话 范围内的变量.applicationScope表示应用范围的变量. 3 -- 表示是否禁用EL语言,TRUE表示禁止.FALSE表示不禁 止.JSP2.0中默认的启用EL语言. 4-- EL语言可显示 逻辑表达式如${true and false}结果是false 关系表达式如${5>6} 结 果是false 算术表达式如 ${5+5} 结果是10 5--EL中的变量搜
Spring AOP中,拦截器责任链处理过程是指当目标方法被多个通知匹配到时,Spring通过引入拦截器链来保证每个通知的正常执行。拦截器链是由一系列的拦截器组成的,每个拦截器都负责在目标方法的前后执行特定的逻辑。 在源码中,拦截器责任链的处理过程主要通过MethodInvocation接口来实现。MethodInvocation接口提供了proceed()方法,用于执行拦截器链中下一个拦截器的逻辑。当调用proceed()方法时,会按照拦截器链的顺序依次执行每个拦截器的逻辑,直到达到链的末尾或者某个拦截器决定终止链的执行。 在拦截器责任链处理过程中,每个拦截器可以在目标方法的调用前后执行自定义的逻辑。拦截器可以对方法的参数进行检查、修改方法的返回值,或者在方法执行前后记录日志等操作。通过拦截器责任链的处理,Spring AOP能够实现面向切面编程的功能。 需要注意的是,拦截器链的执行顺序是根据拦截器的配置顺序确定的。在Spring的配置文件中,可以通过配置拦截器的顺序来控制拦截器链的执行顺序。这样可以确保每个拦截器按照预期的顺序执行,从而达到期望的功能效果。 总结起来,Spring AOP源码的拦截器责任链处理过程主要通过MethodInvocation接口实现,它通过调用proceed()方法来依次执行拦截器链中每个拦截器的逻辑。拦截器链的执行顺序可以通过配置文件来控制,从而实现面向切面编程的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring AOP源码:拦截器责任链处理过程](https://blog.csdn.net/weixin_45031612/article/details/128806966)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Spring AOP 自动代理源码 DefaultAdvisorAutoProxyCreator](https://download.csdn.net/download/weixin_38530536/14854229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [【SpringSpring AOP 源码分析-拦截器链的执行过程(四)](https://blog.csdn.net/qq_46514118/article/details/121912507)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值