上节已经介绍了spring表达式,也举了很多案例,本文是对spring表达式上下文EvaluationContext的一个补充
EvaluationContext在spring表达式中非常重要,里面可以定义数据应该从哪里来 比如 @Value(“#{beanName}”),希望应该可以从spring
中获取单例Bean,都可以由它实现
public interface EvaluationContext {
TypedValue getRootObject();
List<PropertyAccessor> getPropertyAccessors();
List<ConstructorResolver> getConstructorResolvers();
List<MethodResolver> getMethodResolvers();
@Nullable
BeanResolver getBeanResolver();
TypeLocator getTypeLocator();
TypeConverter getTypeConverter();
TypeComparator getTypeComparator();
OperatorOverloader getOperatorOverloader();
void setVariable(String name, @Nullable Object value);
@Nullable
Object lookupVariable(String name);
}
该类的一个重要子类就是StandardEvaluationContext,前面我们也介绍了,默认实现就是该类
本文主要介绍该类的setVariable,registerFunction以及addPropertyAccessor(PropertyAccessor accessor)方法
其中PropertyAccessor是一个属性解析器,SpEl中的属性可以找到匹配的属性解析器,调用其read方法,读取内容,其接口定义如下
public interface PropertyAccessor {
// 就是与StandardEvaluationContext中的rootObject匹配的类型
@Nullable
Class<?>[] getSpecificTargetClasses();
// 返回true,可以读
boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
// 编写读的逻辑
TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
// 不可以写返回false
boolean canWrite(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue)
throws AccessException;
}
接下来我们就自定义一个PropertyAccessor,让其从配置文件shura.properties文件读
public class PropertiesAccessor implements PropertyAccessor {
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[]{Properties.class};
}
@Override
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return true;
}
@Override
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Properties) target).getProperty(name));
}
@Override
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
return false;
}
@Override
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
}
}
上面代码,表示传入的是一个Properties的时候生效,canRead返回true,read方法里面就直接通过Properties.getProperty读取属性值
下面我们以一个例子展示setVariable,registerFunction,addPropertyAccessor的使用
public class ExpressionTest {
// 解析模版
private static final ParserContext parserContext = new ParserContext() {
@Override
public boolean isTemplate() {
return true;
}
@Override
public String getExpressionPrefix() {
return "#{";
}
@Override
public String getExpressionSuffix() {
return "}";
}
};
private final static ExpressionParser parser = new SpelExpressionParser();
private static void evaluate(String text) {
evaluate(text, null);
}
private static void evaluate(String text, StandardEvaluationContext context) {
Expression expression = parser.parseExpression(text, parserContext);
System.out.println(expression.getValue(context));
}
public static void main(String[] args) throws NoSuchMethodException, IOException {
Properties properties = new Properties();
properties.load(Files.newInputStream(ResourceUtils.getFile("classpath:shura.properties").toPath()));
// 构造StandardEvaluationContext,传入的是一个Properties与PropertiesAccessor相匹配
StandardEvaluationContext context = new StandardEvaluationContext(properties);
// addPropertyAccessor
context.addPropertyAccessor(new PropertiesAccessor());
// setVariable
context.setVariable("env", "prod");
// registerFunction
context.registerFunction("long", Long.class.getDeclaredMethod("valueOf", long.class));
// 当前面加上一个#就会从variable去获取,就是上面set的变量以及注册的function
evaluate("#{#env}", context);
evaluate("#{#long('123')}", context);
// 直接写一个字符串,会去找匹配的PropertiesAccessor如果没有匹配会通过反射去找
// 找到PropertiesAccessor执行read方法
evaluate("#{content}", context);
}
}
shura.properties内容
content=hello shura
输出结果
prod
123
hello shura
在spring中,我们给属性上面加上@Value(“#{beanName}”),其实是会根据beanName去找Bean,为什么呢
因为spring中的表达式解析会进入这个方法org.springframework.context.expression.StandardBeanExpressionResolver#evaluate
该方法前面文件也介绍过,源码如下
public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException {
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));
}
this.evaluationCache.put(evalContext, sec);
}
return expr.getValue(sec);
}
catch (Throwable ex) {
throw new BeanExpressionException("Expression parsing failed", ex);
}
}
上面代码可以看出StandardEvaluationContext中的rootObject是BeanExpressionContext类型,并且设置了属性解析器BeanExpressionContextAccessor
我们来看BeanExpressionContextAccessor的逻辑
public class BeanExpressionContextAccessor implements PropertyAccessor {
@Override
public boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
return (target instanceof BeanExpressionContext && ((BeanExpressionContext) target).containsObject(name));
}
@Override
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
Assert.state(target instanceof BeanExpressionContext, "Target must be of type BeanExpressionContext");
return new TypedValue(((BeanExpressionContext) target).getObject(name));
}
@Override
public boolean canWrite(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
return false;
}
@Override
public void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue)
throws AccessException {
throw new AccessException("Beans in a BeanFactory are read-only");
}
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[] {BeanExpressionContext.class};
}
}
通过BeanExpressionContext.getObject(name)获取值,代码如下
public Object getObject(String key) {
if (this.beanFactory.containsBean(key)) {
return this.beanFactory.getBean(key);
}
else if (this.scope != null) {
return this.scope.resolveContextualObject(key);
}
else {
return null;
}
}
实际上就是通过beanFactory.getBean获取的值,到这我们就知道为什么@Value(“#{beanName}”)会去spring中找Bean了
对于SpEl表达式的补充就到这了
欢迎关注,学习不迷路!