java用beaninfo_JavaBeanInfo 和 Spring 之间的关系

Java Beans

在这一章章节中笔者将和各位一起探讨关于 Java Beans 相关的内容。本章主要围绕 java.beans 这个包路径下的代码进行一些说明。

在 Spring 中我们可以看到 BeanInfoFactory 该接口可以用来获取 Class 对应的 BeanInfo 对象,在 CachedIntrospectionResults 中也有相关的成员变量作为信息存储,其他地方还有笔者就不再这里都去搜索了,各位可以自行查阅。

BeanInfoFactory public interface BeanInfoFactory {

@Nullable

BeanInfo getBeanInfo(Class> beanClass) throws IntrospectionException;

}

CachedIntrospectionResults public final class CachedIntrospectionResults {

private final BeanInfo beanInfo;

}

对于 Spring 来说操作 Bean 本身的内容其实是操作各类属性及其提供的方法。从笔者的角度来看 Bean 这个对象我觉得可以分成这三种,第一种是关于 Bean 属性的,第二种是关于属性操作的方法,第三种是提供外部操作的方法。就比如说现在有一个 People 对象,存在多个属性,我们在对这个 Bean 对象定义的时候正常情况下我们会放入 private 修饰的属性名,然后在提供 get 和 set 方法,如果有需要我们可以在通过非属性操作相关的方法。本章就暂时是对前两者的一个讨论。

首先我们来定义一个基本的 Java Bean

public class Student {

private String name;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

现在我们用了一个名字叫做 Student 的 Java 对象,我们来看这个对象的解释

在 Student 对象中具有属性 name

在 Student 对象中存在字段 name

我们通常情况下会有这两种对象的定义解释,那么这个定义解释在 Java 中是如何对其进行定义的呢。在 Java 中有一个接口叫做 java.beans.BeanInfo 这个接口是用来描述 Java Bean 的接口,下面我们来看这个接口中存在的方法

BeanInfo 方法信息

public interface BeanInfo {

BeanDescriptor getBeanDescriptor();

EventSetDescriptor[] getEventSetDescriptors();

int getDefaultEventIndex();

PropertyDescriptor[] getPropertyDescriptors();

int getDefaultPropertyIndex();

MethodDescriptor[] getMethodDescriptors();

BeanInfo[] getAdditionalBeanInfo();

}

getBeanDescriptor :返回 Bean 的描述信息

getEventSetDescriptors:返回 Bean 相关的事件信息

getDefaultEventIndex:返回 Bean 默认事件索引

getPropertyDescriptors:返回 Bean 属性描述

getDefaultPropertyIndex:返回 Bean 默认的属性索引

getMethodDescriptors:返回 Bean 方法描述

getAdditionalBeanInfo :返回其他的 Bean Info 信息

下面我们先来对接口中的返回值做一些介绍

BeanDescriptor 成员变量表

变量名称

变量类型

变量说明

beanClassRef

Reference extends Class>>

bean 的类

customizerClassRef

Reference extends Class>>

自定义的类

PropertyDescriptor 成员变量表

变量名称

变量类型

变量说明

propertyTypeRef

Reference extends Class>>

属性类型

readMethodRef

MethodRef

读方法

writeMethodRef

MethodRef

写方法

propertyEditorClassRef

Reference extends Class>>

属性编辑类型

bound

boolean

constrained

boolean

baseName

String

writeMethodName

String

写方法名称

readMethodName

String

读方法名称

MethodDescriptor 成员变量表

变量名称

变量类型

变量说明

methodRef

MethodRef

方法

paramNames

String[]

参数名称

params

List>>

参数信息

parameterDescriptors

ParameterDescriptor

参数描述

在了解了上述三个对象后我们来编写一个测试用例,该测试用例主要用来获取 BeanInfo 接口数据

BeanInfo 测试用例

@Test

void classTest() throws IntrospectionException {

Class clazz = Student.class;

BeanInfo beanInfo = Introspector.getBeanInfo(clazz);

System.out.println();

}

BeanInfo 信息

b2955528d624fd4c7e74a92f5ee6b438.png

我们现在对 BeanInfo 有了一定的了解,接下来我们要通过 BeanInfo 来创建一个对象,并给该对象进行数据赋值,下面我们来写代码

@NotNull

private T getObject(Class clazz, Map prop) throws Exception {

// 获取 BeanInfo 接口

BeanInfo beanInfo = Introspector.getBeanInfo(clazz);

// 获取 Bean Class

Class> beanClass = beanInfo.getBeanDescriptor().getBeanClass();

// 获取所有的构造函数

Constructor>[] declaredConstructors = beanClass.getDeclaredConstructors();

// 确认构造函数: 直接取无参构造

Constructor constructor = confirmConstructor(declaredConstructors);

// 通过构造函数获取对象

Object bean = constructor.newInstance();

// 为对象设置属性

// 提取属性描述

PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {

// 属性名称

String name = propertyDescriptor.getName();

if (prop.containsKey(name)) {

// 写函数

Method writeMethod = propertyDescriptor.getWriteMethod();

// 从属性表中获取属性名称对应的属性值

Object proValue = prop.get(name);

writeMethod.invoke(bean, proValue);

}

}

return (T) bean;

}

编写这段代码的主要逻辑如下

通过 Class 获取 BeanInfo 接口

通过 BeanInfo 获取 beanClass

通过 beanClass 提取构造函数列表,从构造函数列表中选择一个具体的构造函数(这里采用无参构造的方式)

获取属性描述对象进行属性设置

编写完成后我们来写测试用例

@Test

void beanInfoCreateBean() throws Exception {

Class clazz = Student.class;

// 设置属性表

Map prop = new HashMap<>(8);

prop.put("name", "student_name");

Student bean = getObject(clazz, prop);

assert bean.getName().equals("student_name");

}

这样我们就完成了数据赋值,在上述过程中我们对于 beanClass 的获取可以直接使用参数传递的 clazz 直接使用,可以不需要通过 BeanInfo 接口进行二次调度。

下面我们来谈一谈 Spring 和 BeanInfo 的一些关联。

相信各位在使用 Spring XML 模式的时候会编写下面这样的内容。

这里我们抛开 Spring 中 Bean 生命周期相关的一些接口、占位符解析等内容,我们就简单的来看这个 标签,这个标签定义了一个 class 属性 和子标签 property ,我们可以通过一些 XML 解析工具得到这两个对象,Spring 在这会将 class 属性通过 ClassLoader 转换成 Class 会将 property 转换成对象 PropertyValue ,然后通过反射将对象创建出来。那么这段说明和我们所编写的通过 BeanInfo 创建 Bean 有什么关系呢?我们可以思考下面几个问题

知道了 BeanClass 如何才能创建对象呢?

知道属性名称和属性值如何给对象赋值呢?

这两个问题的答案很简单就是通过 Java 反射机制来进行处理,那么具体怎么做呢?这个问题的答案其实就是我们前面所编写的那段创建对象的代码。回过头我们来看这两个问题

第一个问题的答案:创建对象其本质是寻找构造函数并调用

第二个问题的答案:通过找到写方法将数据写入

Spring 中无参构造函数的调用

ce48d65011319aab413f4cde66f43ad5.png

有参构造的推论过程

28483d4f287eb84c6d3afd0808dca6ae.png

在这里做出了两种构造函数的推论,当完成推论构造函数后就可以进行对象创建及属性赋值了。

属性赋值相关的代码就不在截图了各位可以自行查找。

当我们有了 getObject 这样一个方法后,我们再来看一些生命周期,Spring 当中的各类生命周期其实就是围绕者这段代码的前后来做各种补充操作,

比如 InitializingBean 这个接口的调用时在 Bean 创建完成后,那么我们可以在具体的位置上补充这样一段

修改后的 getObject

@NotNull

private T getObject(Class clazz, Map prop) throws Exception {

// 获取 BeanInfo 接口

BeanInfo beanInfo = Introspector.getBeanInfo(clazz);

// 获取 Bean Class

Class> beanClass = beanInfo.getBeanDescriptor().getBeanClass();

// 获取所有的构造函数

Constructor>[] declaredConstructors = beanClass.getDeclaredConstructors();

// 确认构造函数: 直接取无参构造

Constructor constructor = confirmConstructor(declaredConstructors);

// 通过构造函数获取对象

Object bean = constructor.newInstance();

// 为对象设置属性

// 提取属性描述

PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {

// 属性名称

String name = propertyDescriptor.getName();

if (prop.containsKey(name)) {

// 写函数

Method writeMethod = propertyDescriptor.getWriteMethod();

// 从属性表中获取属性名称对应的属性值

Object proValue = prop.get(name);

writeMethod.invoke(bean, proValue);

}

}

if (bean instanceof InitializingBean) {

((InitializingBean) bean).afterPropertiesSet();

}

return (T) bean;

}

其他的一些生命周期接口也是可以通过类似的处理方式进行补充,笔者就不在这里进行展开了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值