问题(已解决):Spring-Expression中,SpelNodeImpl类的子类BeanReference对应什么场景?

【背景】

在研究SpEL表达式漏洞官方推出的增强方案时发现,官方推出的SimpleEvaluationContext.java对比StandardEvaluationContext.java修改了一些东西。其中一个就是修改了getBeanResolver()方法的实现,将其返回置为空。分析源码以后,我猜测:SimpleEvaluationContext.java重写getBeanResolver()方法的实现,目的是为了禁止在bean中使用表达式。getBeanResolver()方法调用图如下(虚线表示接口或抽象类的方法):
在这里插入图片描述

【问题描述】

为了验证这个猜想,我写了一个使用bean中使用SpEL的demo,但是发现demo运行时,并没有构造BeanReference类的实例,更没有调用到BeanReference.getValueInternal()方法以及EvaluationContext.getBeanResolver()方法。因此,BeanReference类的作用是什么呢?在什么情况下才能触发这个类的构造以及BeanReference.getValueInternal()方法的执行呢?
我用来测试的demo如下:
Inventor.java(Bean的java对象)

package com.power.bean;

import java.util.Date;
import java.util.GregorianCalendar;

/**
 * @author power
 * @date 2018/10/8 下午2:34
 */
public class Inventor {
    private String name;
    private Date birthday;
    private String nationality;
    private Object random;

    public Inventor() {}

    public Inventor(String name, Date birthday, String nationality) {
        this.name = name;
        this.birthday = birthday;
        this.nationality = nationality;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public Object getRandom() {
        return random;
    }

    public void setRandom(Object random) {
        this.random = random;
    }
}

bean的配置文件spel01.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

<bean id="test" class="com.power.bean.Inventor">
    <!--<property name="random" value="#{T(java.lang.Math).random()*100}"></property>-->
    <property name="random" value="1000"></property>
</bean>

<bean id = "Tesla" class="com.power.bean.Inventor">
    <!--<property name="name" value="#{test.name}"></property>-->
    <property name="name" value="#{'Hello'+ 'World'}"></property>
   <!-- <property name="birthday" value="1972-09-01"></property>-->
    <property name="nationality" value="Serbian"></property>
    <property name="random" value="#{test.random}"></property>
</bean>
测试类test.java:
package com.power.spel;

import com.power.bean.Inventor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.GregorianCalendar;

/**
 * @author power
 * @date 2018/10/8 下午2:32
 */
public class SpEL {
    public void test() {
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.set(1856,7,9);

        //Inventor tesla = new Inventor("Nikola Tesla", calendar.getTime(), "Serbian");
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spel01.xml");
        Inventor tesla = ctx.getBean("Tesla",Inventor.class);

        System.out.println(tesla.getRandom());
    }

    public static void main(String[] args) {
        SpEL demo = new SpEL();
        demo.test();
    }
}

【实验情况】

最终实验的时候,SpelNodeImpl.getValue()并未调用BeanReference,getValueInternal()的实现,调用的是CompoundExpression.getValueInternal()。且整个执行过程均未调用到BeanReference.getValueInternal()
在这里插入图片描述
【问题出现原因】
最后,重新阅读了Spring-Expression的官方文档以后发现:此处的BeanReference的应用场景不是在BeanDefinition中使用表达式,而是允许用户自定义的BeanResolver,处理ExpressionParser.parseExpression(String expression)中的expression。具体说明见:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions

  1. test.java
package com.power.spel;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/**
 * @author power
 * @date 2018/10/10 上午10:39
 */
public class BeanReferenceTest {
    public void test() {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(new MyBeanResolver());

        Object bean = parser.parseExpression("&foo").getValue(context);
        System.out.println(bean);
    }

    public static void main(String[] args) {
        BeanReferenceTest demo = new BeanReferenceTest();
        demo.test();
    }
}
  1. MyBeanResolver.java
package com.power.spel;

import org.springframework.expression.AccessException;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.EvaluationContext;

/**
 * @author power
 * @date 2018/10/9 下午9:09
 */
public class MyBeanResolver implements BeanResolver {
    @Override
    public Object resolve(EvaluationContext context, String beanName) throws AccessException {
        if (beanName.equals("foo") || beanName.equals("bar")) {
            return "MyBeanResolver";
        }
        if (beanName.equals("&foo")) {
            return "MyBeanResolver-&foo";
        }
        throw new AccessException("not heard of " + beanName);
    }
}

版权声明:本文为u014012107原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。