【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 来使用的,希望能在今后的开发工作中尽可能使用,因为它功能实在很强大