闲聊spring源码六(SpEL表达式)

概述

spring 版本 6.0.12 xml方式
org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory 中关于表达式解析的原理

相关链接

在阅读文章前,可以浏览一下

spring官方文档链接
案例中详细请移步spring系列文章一 此处

两个例子

通过两个例子,深入了解一下几个类
org.springframework.expression.Expression
org.springframework.expression.ExpressionParser及相关实现类
org.springframework.expression.spel.standard.Token

代码

测试时使用的代码

// pojo
@Component
public class TestBean {

	// 用户名
	private String name;
	// 年龄
	private int age;
	// 按年龄分 青年,中年,老年
	private String type;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public void print() {
		System.out.println("test bean method ...");
		System.out.println(this);
		System.out.println("spring源码环境构建完成");
	}

	@Override
	public String toString() {
		return "TestBean{" +
				"name='" + name + '\'' +
				", age=" + age +
				", type='" + type + '\'' +
				'}';
	}
}
// 测试类
package org.springframework.test.my;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpELTest {
	public static void main(String[] args) {
		//helloWorld();

		strMethod();
	}

	/**
	 * 'Hello World'.concat('!')
	 * 解析成 Token集合
	 * 'Hello World'
	 * .  DOT
	 * concat  IDENTIFIER
	 * [LPAREN(()](20,21)
	 * [LITERAL_STRING:'!'](21,24)
	 * [RPAREN())](24,25)
	 */
	private static void strMethod() {
		ExpressionParser parser = new SpelExpressionParser();
		Expression exp = parser.parseExpression("'Hello World'.concat('!')");
		String message = (String) exp.getValue();
		System.out.println(message);
	}

	private static void helloWorld() {
		SpelExpressionParser parser = new SpelExpressionParser();
		Expression expression = parser.parseExpression("'hello world'");
		String value = (String) expression.getValue();
		System.out.println(value);
	}

通过 strMethod() 方法进行代码调试,注意下面源码关键处,将在这些地方打上断点
重要步骤概括: 将表达式转成 Token 集合,将 token 转换成对应的什么 (是字符串,构造器,方法,还是属性) 引用,从表达式中获取值

源代码定位

根据以下全类名,在idea中快速定位 ,打上断点

// 重点:这个类spring封装中也是重点
org.springframework.expression.spel.standard.InternalSpelExpressionParser#doParseExpression
// 将表达式转成 Token 集合
org.springframework.expression.spel.standard.Tokenizer#process
// 将 token 转换成对应的 是字符串,构造器,方法,还是属性 引用
org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatExpression
// 从表达式中获取值 
org.springframework.expression.spel.ast.CompoundExpression#getValueInternal
// 如果是方法,则执行此方法
org.springframework.expression.spel.ast.MethodReference#getValueInternal(org.springframework.expression.EvaluationContext, java.lang.Object, org.springframework.core.convert.TypeDescriptor, java.lang.Object[])

调试代码

调试代码,关键的地方截图标记文字,降低源码阅读难度

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述

从表达式中获取值
此处代码定位org.springframework.expression.spel.ast.MethodReference#getValueInternal(org.springframework.expression.EvaluationContext, java.lang.Object, org.springframework.core.convert.TypeDescriptor, java.lang.Object[])

在这里插入图片描述

spring

spring在此处配置了表达解析相关类

源码定准

根据以下全类名,在idea中快速定位,打上断点

org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
// 设置EL表达式解析器(Bean初始化完成后填充属性时会用到)
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

在这里插入图片描述

spring断点

在下面几处打上断点

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
org.springframework.context.expression.StandardBeanExpressionResolver#evaluate
// 此处就已和自己构建表达式解析一样的逻辑
org.springframework.expression.spel.standard.InternalSpelExpressionParser#doParseExpression

在这里插入图片描述
最终结果
在这里插入图片描述

结束

本文主要概述了 springSpEL表达式 的源理,在指出处快速打上断点,可以快速的进行代码调试,表达式解析过程中有嵌套调用及递归,调试过程需要一定的耐心

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流月up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值