13 KiB
PropertyAccessor
一、基本信息
✒️ 作者 - Lex 📝 博客 - 掘金 📚 源码地址 - github
二、知识储备
-
Spring 表达式语言(SpEL)
- 了解 SpEL 的基本语法和功能,包括表达式的解析、运算符、变量、函数等。
-
对象属性访问与反射机制
- 了解如何使用 Java 的反射机制来访问和操作对象的属性,包括获取类的字段、调用对象的方法、设置字段的值等。
-
设计模式
- 理解常见的设计模式,如适配器模式、策略模式等,这些设计模式在实现自定义
PropertyAccessor
接口时可能会用到。
- 理解常见的设计模式,如适配器模式、策略模式等,这些设计模式在实现自定义
三、基本描述
PropertyAccessor
接口的作用在于定义了一套统一的属性访问规范,通过该接口可以实现对对象属性的读取和写入操作,使得 Spring 框架中的各个模块能够统一地访问对象的属性,从而实现更加灵活和可扩展的应用开发。通过实现这个接口,可以定制化对象属性的访问逻辑,以满足不同场景下的需求,例如在 Spring 表达式语言(SpEL)、数据绑定、AOP 编程等方面发挥作用。
四、主要功能
-
属性读取
- 提供了
canRead()
方法用于判断是否可以读取指定属性,以及read()
方法用于读取属性的值。这使得可以通过统一的方式从对象中获取属性值。
- 提供了
-
属性写入
- 提供了
canWrite()
方法用于判断是否可以写入指定属性,以及write()
方法用于设置属性的值。这使得可以通过统一的方式向对象中设置属性值。
- 提供了
-
自定义属性访问逻辑
- 允许我们根据实际需求实现自定义的属性访问逻辑,例如通过自定义的
PropertyAccessor
实现类来访问对象的属性,从而实现特定的业务逻辑或行为。
- 允许我们根据实际需求实现自定义的属性访问逻辑,例如通过自定义的
-
适配不同的对象结构
- 可以针对不同的对象结构实现不同的
PropertyAccessor
实现类,以适应不同类型的对象,包括 JavaBean、Map、数组等。
- 可以针对不同的对象结构实现不同的
五、接口源码
PropertyAccessor
的接口,它用于读取和写入对象的属性。接口中包含了四个方法,分别用于判断是否能够读取属性、读取属性值、判断是否能够写入属性以及写入属性值。此外,接口中还包含了一个用于获取特定目标类数组的方法。该接口提供了一种统一的方式来访问对象的属性,使得可以实现自定义的属性访问逻辑,并能够适配不同的对象结构。
/**
* 属性访问器能够读取(可能写入)对象的属性。
*
* <p>这个接口没有限制,因此实现者可以自由地直接访问属性作为字段,或通过getter方法或其他方式访问属性。
*
* <p>解析器可以选择指定一个目标类的数组,用于调用它。然而,如果从{@link #getSpecificTargetClasses()}返回{@code null},它将被调用
* 以尝试解析所有属性引用,并有机会确定它是否可以读取或写入它们。
*
* <p>属性解析器被认为是有序的,并且每个都将依次被调用。影响调用顺序的唯一规则是,在{@link #getSpecificTargetClasses()}中直接命名目标类的任何解析器
* 将在通用解析器之前首先被调用。
*
* @author Andy Clement
* @since 3.0
*/
public interface PropertyAccessor {
/**
* 返回此解析器应该被调用的类数组。
* <p>返回{@code null}表示这是一个通用解析器,可以在任何类型上尝试解析属性。
* @return 适用于此解析器的类数组(或{@code null}如果是通用解析器)
*/
@Nullable
Class<?>[] getSpecificTargetClasses();
/**
* 用于确定解析器实例是否能够访问指定目标对象上的指定属性。
* @param context 尝试进行访问的评估上下文
* @param target 要访问属性的目标对象
* @param name 要访问的属性的名称
* @return 如果此解析器能够读取属性,则为true
* @throws AccessException 如果有任何问题确定是否可以读取属性
*/
boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
/**
* 用于从指定的目标对象中读取属性。
* 只有当{@link #canRead}也返回{@code true}时才能成功。
* @param context 尝试进行访问的评估上下文
* @param target 要访问属性的目标对象
* @param name 要访问的属性的名称
* @return 包装读取的属性值和其类型描述符的TypedValue对象
* @throws AccessException 如果有任何问题访问属性值
*/
TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
/**
* 用于确定解析器实例是否能够向指定目标对象上的指定属性写入。
* @param context 尝试进行访问的评估上下文
* @param target 要访问属性的目标对象
* @param name 要访问的属性的名称
* @return 如果此解析器能够写入属性,则为true
* @throws AccessException 如果有任何问题确定是否可以写入属性
*/
boolean canWrite(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
/**
* 用于向指定目标对象的属性写入。
* 只有当{@link #canWrite}也返回{@code true}时才能成功。
* @param context 尝试进行访问的评估上下文
* @param target 要访问属性的目标对象
* @param name 要访问的属性的名称
* @param newValue 属性的新值
* @throws AccessException 如果有任何问题写入属性值
*/
void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue)
throws AccessException;
}
六、主要实现
-
BeanExpressionContextAccessor
- 允许在表达式中访问特定的对象和属性。通过
BeanExpressionContextAccessor
,可以在 SpEL 表达式中直接访问上下文中的对象属性,这在某些场景下非常有用,例如在 Spring 的@Value
注解中使用 SpEL 表达式读取配置属性。
- 允许在表达式中访问特定的对象和属性。通过
-
BeanFactoryAccessor
- 允许在 SpEL 表达式中访问 Spring
BeanFactory
中注册的 Bean 对象的属性。BeanFactory
是 Spring IoC 容器的核心接口,用于管理和创建 Bean。通过BeanFactoryAccessor
,可以在 SpEL 表达式中访问 IoC 容器中注册的 Bean,读取和设置其属性值,这为在 SpEL 中访问 Spring Bean 提供了便利。
- 允许在 SpEL 表达式中访问 Spring
-
CompilablePropertyAccessor
- 允许对属性访问进行优化以提高性能。通过编译能力,可以在运行时对属性访问逻辑进行优化,从而减少不必要的计算和提高执行效率,这在某些性能要求较高的场景下非常有用。
-
DataBindingPropertyAccessor
- 允许在 Spring 应用程序中读取和写入属性。数据绑定是将用户输入绑定到后端数据模型的过程,在 Web 应用程序中广泛应用。
DataBindingPropertyAccessor
提供了方便的方式来读取和设置对象的属性值,用于处理数据绑定过程中的属性访问需求。
- 允许在 Spring 应用程序中读取和写入属性。数据绑定是将用户输入绑定到后端数据模型的过程,在 Web 应用程序中广泛应用。
-
EnvironmentAccessor
EnvironmentAccessor
用于从 Spring 环境中访问属性,例如读取配置文件中的属性值。Spring 的环境抽象提供了一种统一的方式来访问应用程序的环境属性,包括系统属性、环境变量、配置文件等。通过EnvironmentAccessor
,可以在 SpEL 表达式中直接访问 Spring 环境中的属性值,从而实现动态的配置和属性注入。
-
JspPropertyAccessor
- 允许在 JSP 页面中使用 SpEL 表达式,并在 JSP 标签中访问对象的属性。JSP 是一种常见的 Web 视图技术,与 SpEL 结合使用可以实现更加灵活和动态的页面渲染。通过在 JSP 中使用 SpEL 表达式,并在自定义标签(如
eval
标签)内访问对象的属性,可以实现更加灵活和可配置的页面逻辑。
- 允许在 JSP 页面中使用 SpEL 表达式,并在 JSP 标签中访问对象的属性。JSP 是一种常见的 Web 视图技术,与 SpEL 结合使用可以实现更加灵活和动态的页面渲染。通过在 JSP 中使用 SpEL 表达式,并在自定义标签(如
-
MapAccessor
MapAccessor
用于在 SpEL 表达式中直接访问 Map 对象中的键值对。Map 是一种常见的数据结构,用于存储键值对的集合。通过MapAccessor
,可以在 SpEL 表达式中直接访问 Map 对象中的键值对,读取和设置其属性值,这为在 SpEL 中操作 Map 提供了便利。
-
OptimalPropertyAccessor
OptimalPropertyAccessor
提供了属性访问的优化实现,通常在ReflectivePropertyAccessor
内部使用,用于提高属性访问的性能。通过对属性访问逻辑的优化,可以减少不必要的计算和提高执行效率,从而更加高效地访问对象的属性值。
七、最佳实践
创建了一个 SpEL 表达式解析器对象 ExpressionParser
,然后创建了一个 StandardEvaluationContext
对象作为 SpEL 表达式的上下文。在上下文中设置了一个名为 myBean
的变量,其值为一个 MyBean
对象。接着,通过 parser.parseExpression("#myBean.name")
解析了一个 SpEL 表达式,该表达式表示访问 myBean
对象的 name
属性。最后,通过 getValue(context, String.class)
方法获取了属性值,并将其输出。
public class PropertyAccessorDemo {
public static void main(String[] args) {
// 创建一个SpEL表达式解析器
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("myBean",new MyBean("spring-reading"));
// 解析SpEL表达式,并使用构造函数实例化对象
String name = parser.parseExpression("#myBean.name").getValue(context, String.class);
System.out.println("name = " + name);
}
}
MyBean
类定义了一个简单的 Java Bean,具有一个名为 name
的私有字段和相应的 getter 和 setter 方法。
public class MyBean {
private String name;
public MyBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
'}';
}
}
运行结果,通过 SpEL 表达式解析器访问了对象的属性,并得到了属性值 "spring-reading"。
name = spring-reading
八、与其他组件的关系
-
Spring 表达式语言(SpEL)
- 在 SpEL 中,
PropertyAccessor
接口被用于访问对象的属性。SpEL 提供了一种在运行时动态求值表达式的机制,通过PropertyAccessor
接口可以实现对对象属性的读写操作。
- 在 SpEL 中,
-
Spring 数据绑定
- 在 Spring 中,数据绑定是一种常见的操作,用于将用户提交的数据绑定到后端数据模型上。
PropertyAccessor
接口提供了一种统一的方式来访问对象的属性,可以在数据绑定过程中使用。
- 在 Spring 中,数据绑定是一种常见的操作,用于将用户提交的数据绑定到后端数据模型上。
-
Spring AOP 编程
- 在 Spring 的面向切面编程(AOP)中,
PropertyAccessor
接口可以用于访问被增强对象的属性。通过 AOP 可以实现对被增强对象的属性进行拦截、修改等操作。
- 在 Spring 的面向切面编程(AOP)中,
-
Spring IOC 容器
- 在 Spring 的控制反转(IoC)容器中,
PropertyAccessor
接口可以用于访问注册在容器中的 Bean 对象的属性。通过 IoC 容器,可以方便地管理和访问对象的属性。
- 在 Spring 的控制反转(IoC)容器中,
-
其他自定义组件
- 除了以上提到的场景之外,
PropertyAccessor
接口还可以被自定义组件使用。例如,在自定义的框架或库中,可以使用PropertyAccessor
接口来实现对对象属性的访问操作,以实现特定的功能或逻辑。
- 除了以上提到的场景之外,
九、常见问题
-
属性访问权限问题
- 在使用
PropertyAccessor
接口时,可能会遇到对象的属性访问权限不足的问题,导致无法读取或写入属性。这可能涉及到对象的访问控制和权限设置。
- 在使用
-
属性命名问题
- 在访问对象的属性时,可能会出现属性命名不一致的问题,例如大小写不匹配、拼写错误等,导致无法正确访问属性。
-
属性类型转换问题
- 在进行属性读取或写入时,可能会遇到属性类型转换失败的问题,例如尝试将一个字符串值赋给一个整数类型的属性,导致转换异常。
-
上下文环境配置问题
- 在使用
PropertyAccessor
接口时,需要提供一个合适的上下文环境(如EvaluationContext
),可能会出现配置错误或环境设置不当的问题,导致属性访问失败。
- 在使用
-
异常处理问题
- 在属性访问过程中,可能会出现各种异常情况,例如空指针异常、访问权限异常等,需要进行适当的异常处理以保证程序的稳定性和可靠性。