【Spring】SpEL 二 PropertyAccessor 相关
前言
本章节介绍 PropertyAccessor
相关类
PropertyAccessor
public interface PropertyAccessor {
@Nullable
// 指定目标类型
Class<?>[] getSpecificTargetClasses();
// 是否可读
boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
// 读操作
TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
// 是否可写
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;
}
属性操作相关,即如何通过 表达式
定位、操作对应属性
继承图如上:
ReflectivePropertyAccessor
,基于反射操作BeanFactoryAccessor
,依赖BeanFactory
进行操作EnvironmentAccessor
,依赖Environment
进行操作BeanExpressionContextAccessor
,依赖BeanExpressionContext
进行操作- 等等
BeanFactoryAccessor
public class BeanFactoryAccessor implements PropertyAccessor {
// 数据源类型指定为 BeanFactory
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[] {BeanFactory.class};
}
// BeanFactory 中要有对应的 bean
@Override
public boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
return (target instanceof BeanFactory && ((BeanFactory) target).containsBean(name));
}
// 获取对应的 bean
@Override
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
Assert.state(target instanceof BeanFactory, "Target must be of type BeanFactory");
return new TypedValue(((BeanFactory) target).getBean(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");
}
}
BeanFactoryAccessor
的实现很简单,就是基于BeanFactory
进行操作,注意是只可读不可写EnvironmentAccessor
的实现也类似,读操作委托给getProperty
方法,同样不可写
StandardEvaluationContext
上一章节中,我们使用最频繁的 解析上下文
就是 StandardEvaluationContext
,它内部便是维护了一组 PropertyAccessor
,当然如果不指定,其默认维护一个 ReflectivePropertyAccessor
,源码如下:
private List<PropertyAccessor> initPropertyAccessors() {
List<PropertyAccessor> accessors = this.propertyAccessors;
if (accessors == null) {
accessors = new ArrayList<>(5);
accessors.add(new ReflectivePropertyAccessor());
this.propertyAccessors = accessors;
}
return accessors;
}
所以上一章节我们解析的表达式,都是基于 反射
操作对应对象、属性了
接下来,我们就基于 BeanFactoryAccessor
和 EnvironmentAccessor
给出示例
示例
@SpringBootTest
@ContextConfiguration(classes = { PropertyAccessorTest.class })
public class PropertyAccessorTest {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
@Autowired
BeanFactory beanFactory;
@Autowired
Environment environment;
@Bean
public A a() {
return new A();
}
@Test
public void bean() {
context.addPropertyAccessor(new BeanFactoryAccessor());
context.setRootObject(beanFactory);
A a = parser
.parseExpression("a")
.getValue(context, A.class);
Assertions.assertNotNull(a);
}
@Test
public void env() {
context.addPropertyAccessor(new EnvironmentAccessor());
context.setRootObject(environment);
System.out.println(parser
.parseExpression("user")
.getValue(context, String.class));
}
}
StandardEvaluationContext
类提供方法addPropertyAccessor
,可以额外添加PropertyAccessor
- 示例中注册了名为
a
的bean
,再上下文中添加对应的BeanFactoryAccessor
后,就可以解析到表达式a
了 - 同理,添加对应
EnvironmentAccessor
,可以从EnvironmentAccessor
中获取对应属性,示例中的user
是SystemProperties
中的属性 - 同样,我们可以实现
PropertyAccessor
来自定义拓展,可参考文末给出的链接
总结
本章节主要介绍了 PropertyAccessor
类相关,重点示例了 BeanFactoryAccessor
和 EnvironmentAccessor
的用法,可以基于 Spring
环境进行表达式解析
深入了解这块知识最初的目的是为 Spring
处理 表达式
做铺垫,结果发现它也是可以解耦 Spring
来使用的,希望能在今后的开发工作中尽可能使用,因为它功能实在很强大
相关参考
【小家Spring】SpEL你感兴趣的实现原理浅析spring-expression~(SpelExpressionParser、EvaluationContext、rootObject)