“见贤思齐焉,见不贤而内自省也”。新的一年,我们从内省开始。
在传统儒家思想中,人们应在日常生活中不断反思自己的行为,向优秀的人学习,同时审视自身的不足之处,才能实现个人的成长与进步。
新年伊始,我们也都会停下匆忙的脚步,去回顾过去一年的经历,思考哪些事情做得好,哪些地方还有待改进。这种自我反省与复盘,其实就是“内省”的体现。通过不断地“内省”,我们可以更好地理解自己,制定更加明确的目标和计划,找到前进的方向,迎接未来的挑战。
在 Java 中,也有“内省”的概念。它虽与儒家提倡的精神内核不同,但在功能上却有着异曲同工之妙 😂。
什么是内省?
在计算机科学中,内省是指程序运行时检查对象类型的能力。在 Java 中,内省机制是一种用于处理符合 JavaBean 规范的类的方法。它提供了一套 API,允许我们通过程序化的方式访问和操作类的属性、方法及事件。通过这种方式,我们可以在不知道具体实现细节的情况下,动态地访问或修改对象的状态。
内省与反射
反射是一种更为通用的技术,指程序在运行时通过检查或“自检”其结构,动态修改自身行为的能力。通过反射,我们可以动态地加载类、创建对象实例、调用方法以及访问字段等,并且反射是可以应用于任何类的,无论该类是否遵循了 JavaBean 规范; 内省其实是基于反射实现的,只是提供了更高层次的抽象,它更专注于对 JavaBean 的操作,使得操作 JavaBean 更加方便高效。例如,当我们想要获取某个类的所有公共字段时,可以使用反射直接获取 Field
对象数组;但如果操作的目标符合 JavaBean 规范,则应该优先考虑使用内省机制。
什么是 JavaBean
JavaBean 是指符合特定规范的 Java 类,主要用于封装数据。成为一个 JavaBean 需要满足以下这几个条件:
无参构造方法:必须有一个无参构造方法,确保可以通过默认构造器创建实例。 私有化属性:所有属性须使用 private
访问修饰符封装,防止外部随意更改类的内部状态。提供公共的 getter 和 setter 方法:为每个属性定义相应的 public
读写方法,并遵循命名约定(如getName()
和setName(String name)
)。序列化支持:实现 Serializable
接口,以保证对象能够序列化为字节流便于存储或者网络传输。
内省机制的工作原理
核心组件
Java 内省机制主要依赖于 java.beans
包下的几个关键类和接口。以下是其中几个重要的组成部分:
Introspector 类:内省机制的核心类,提供了静态方法 getBeanInfo(Class<?> beanClass)
来获取给定类型的 JavaBean 信息。该方法返回一个BeanInfo
对象,对象中包含了关于此 JavaBean 的全面描述,包括所有属性、方法和事件等信息。BeanInfo 接口:JavaBean 信息的顶层抽象,定义了如何描述一个 JavaBean 的行为,用于提供 JavaBean 的元数据。通过 BeanInfo
,我们可以获取 Bean 的属性描述符(PropertyDescriptor[] getPropertyDescriptors()
)、方法描述符(MethodDescriptor[] getMethodDescriptors()
)以及其他相关信息。PropertyDescriptor 类:对于 JavaBean 的每一个属性,都会有一个对应的 PropertyDescriptor
实例。该类不仅封装了属性的基本信息(如名称、类型),还提供了对 getter 和 setter 方法的访问途径。获取到PropertyDescriptor
实例后,就可以实现 JavaBean 的属性值读写了。
基本用法
下面,我们通过具体的例子来演示如何使用 Java 内省机制进行基本的操作。
public class IntrospectorBasicUsage {
public static void main(String[] args) throws Exception {
User user = new User("张三", 25);
// 获取 BeanInfo 对象
BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
// 获取所有属性描述符
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : propertyDescriptors) {
if (!"class".equals(pd.getName())) {
// 获取属性的 get 方法
Method readMethod = pd.getReadMethod();
if (readMethod != null) {
// 调用 get 方法获取属性值
Object value = readMethod.invoke(user);
System.out.println("属性名: " + pd.getName() + ", 属性值: " + value);
}
}
}
// 修改 name 属性的值
PropertyDescriptor pd = new PropertyDescriptor("name", User.class);
Method writeMethod = pd.getWriteMethod();
if (writeMethod != null) {
writeMethod.invoke(user, "李四");
}
// 验证修改是否成功
Method readMethod = pd.getReadMethod();
if (readMethod != null) {
Object newValue = readMethod.invoke(user);
System.out.println("'name' 的新值为: " + newValue);
}
}
}
class User {
private String name;
privateint age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
示例展示了如何利用 Introspector
类获取 User
类的所有属性,并通过调用 getter 方法读取它们的值。这里的关键在于 getPropertyDescriptors()
方法返回了一个 PropertyDescriptor
数组,数组中的每个元素代表了类中的一个属性。我们可以通过 getReadMethod()
来获得相应的 getter 方法,getWriteMethod()
来获取其 setter 方法,并使用反射机制调用它以获取属性的实际值。
内省在 Spring 框架中的应用
依赖注入:Spring 利用了 Java 的内省机制来自动发现和管理 Spring Bean 之间的依赖关系。 BeanWrapper
接口是 Spring 用来操作 Java Bean 属性的核心类。通过其子类,Spring 可以在运行时动态地获取和设置 Spring Bean 的属性值。
org.springframework.beans.BeanWrapperImpl#getPropertyDescriptor
:
public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
String finalPath = getFinalPath(nestedBw, propertyName);
PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found");
}
return pd;
}
BeanUtils:Spring BeanUtils 中的 copyProperties()
这个方法也是依赖于 Java 的内省机制实现,从而可以实现动态地获取源对象和目标对象的属性描述符,并执行相应的 getter 和 setter 方法进行属性值的转移,实现了 Java 对象之间属性值的便捷复制。
org.springframework.beans.BeanUtils#copyProperties(java.lang.Object, java.lang.Object, java.lang.Class<?>, java.lang.String...)
:
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
thrownew IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
CachedIntrospectionResults.forClass(source.getClass()) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = (sourceResults != null ?
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
if (isAssignable(writeMethod, readMethod)) {
try {
ReflectionUtils.makeAccessible(readMethod);
Object value = readMethod.invoke(source);
ReflectionUtils.makeAccessible(writeMethod);
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
thrownew FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
}
内省机制最佳实践
遵循 JavaBean 规范:务必正确实现 getter/setter 方法,并保持属性名的一致性,这样可以保证内省机制能够准确无误地识别并操作对象。 最小化反射调用:尽可能减少对 getDeclaredMethods()
、getDeclaredFields()
等方法的调用次数,因为每次调用都会带来额外的性能成本。使用缓存机制:如果多次访问相同的类信息,可以将其结果缓存起来以供后续重用。
好了,Java 内省机制的介绍到这里就结束了。其实,无论是个人成长还是精进技术,内省都是至关重要的。
在新的一年里,让我们不仅在生活上践行内省的价值观,在技术探索的路上也同样秉持着这一精神,不断地学习新的知识和技术,迎接每一个挑战,抓住每一次机遇,共同成长,共创辉煌。
再一次祝福大家:新年快乐!!!
您的鼓励对我持续创作非常关键,如果本文对您有帮助,请记得点赞、分享、在看哦~~~谢谢!