SpEl及@Value的SpEl表达式源码分析

Posted jazon@

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpEl及@Value的SpEl表达式源码分析相关的知识,希望对你有一定的参考价值。

SpEl使用例子

public class SpElDemo 

    public static void main(String[] args) 
        Stu stu = new Stu();
        stu.setName("xxl");
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
        standardEvaluationContext.setRootObject(new StuHandler());
        standardEvaluationContext.setVariable("stu", stu);
        String ret = spelExpressionParser.parseExpression("println(#stu)").getValue(standardEvaluationContext, String.class);
        String retStr = spelExpressionParser.parseExpression("#stu.myAge()").getValue(standardEvaluationContext, String.class);
        String retStr2 = spelExpressionParser.parseExpression("test.myAge()").getValue(standardEvaluationContext, String.class);
        System.out.println(ret);
        System.out.println(retStr);
        System.out.println(retStr2);
    

public class Stu 
    private String name;
    
    public String myAge() 
        System.out.println("myage");
        return "myage";
    

public class StuHandler 

    private Stu test = new Stu();

    public String println(Stu stu) 
        System.out.println(stu);
        return "finished";
    

输出结果

Stu(name=xxl)
myage
myage
finished
myage
myage

EvaluationContext: 表达式执行的上下文环境,一般实现有StandardEvaluationContext,表达式解析时将会使用context的根对象(RootObject),变量(Variable)等。MethodBasedEvaluationContext继承了StandardEvaluationContext,会将method的变量载入到variable之中
默认情况下, 由于println方法前没有指定对象,println方法指向的是RootObject,如果要调用其他对象的,需要将对象设置到Variable中,并指定对象#stu.myAge()。如果属性前不加#,如test.myAge()则是访问到rootObject的属性。

@Value使用SpEl解析源码分析

关键方法:org.springframework.context.expression.StandardBeanExpressionResolver#evaluate

public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException 
		if (!StringUtils.hasLength(value)) 
			return value;
		
		try 
            // 获取Expression
			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) 
                // 设置RootObject为BeanExpressionContext
				sec = new StandardEvaluationContext(evalContext);
                // 属性访问器,用于访问RootObject的属性,可以设置多个,这里生效的就第一个
                // 适用于RootObject为BeanExpressionContext的属性访问器
				sec.addPropertyAccessor(new BeanExpressionContextAccessor());
                // 适用于RootObject为BeanFactory的属性访问器
				sec.addPropertyAccessor(new BeanFactoryAccessor());
                // 适用于RootObject为Map的属性访问器
				sec.addPropertyAccessor(new MapAccessor());
                // 适用于RootObject为Environment的属性访问器
				sec.addPropertyAccessor(new EnvironmentAccessor());
                // bean查找器,在SpEl中要找一个bean可以使用@beanName或者&beanName,底层就是使用的BeanResolver去找的
				sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory()));
                // TypeLocator底层确定使用的classLoader,
				sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader()));
				ConversionService conversionService = evalContext.getBeanFactory().getConversionService();
				if (conversionService != null) 
                    // spel解析过程使用的类型转换
					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);
		
	

以上是关于SpEl及@Value的SpEl表达式源码分析的主要内容,如果未能解决你的问题,请参考以下文章

SpEl及@Value的SpEl表达式源码分析

SpEl及@Value的SpEl表达式源码分析

带有@Value 的 Spring 表达式语言 (SpEL):美元与哈希($ 与 #)

Spring学习笔记--Spring表达式语言SpEL

Spring之@Value详解,利用SPEL注入List

SpringBoot基础系列@Value 之字面量及 SpEL使用知识点介绍篇