比反射更好用的內(nèi)省,你知道嗎?
內(nèi)省是基于反射實現(xiàn)的,主要用于操作符合JavaBean規(guī)范的類。
JavaBean是一種特殊的Java類,通常用于封裝多個屬性為一個單一的對象。我們再復(fù)習(xí)下JavaBean規(guī)范:
- 必須要有一個公共無參構(gòu)造;
- Javabean類不應(yīng)有公共屬性,屬性都應(yīng)該是 private;
- 為私有(private聲明)屬性提供符合命名規(guī)范的getter/setter方法;
- 應(yīng)該要實現(xiàn)Serializeable接口(內(nèi)省并不強要求這一條);
- javaBean類必須是一個公共類,將其訪問屬性設(shè)置為public。
內(nèi)省機制通過反射獲取屬性描述器(PropertyDescriptor),然后可以方便地獲取和設(shè)置屬性值。
內(nèi)省操作只針對JavaBean,只有符合JavaBean規(guī)則的類的成員才可以采用內(nèi)省API進(jìn)行操作。
內(nèi)省的核心類在java.beans包下,主要類如下:
java.beans包
初識內(nèi)省
內(nèi)省就是轉(zhuǎn)為JavaBean準(zhǔn)備的,主要包括下面幾個類:
- Introspector類:這是內(nèi)省API的核心類,提供了獲取BeanInfo對象的方法,例如Introspector.getBeanInfo()方法。
- BeanInfo類:這個類包含了關(guān)于一個對象的所有Bean屬性信息,包括屬性的描述符(PropertyDescriptor)。
- PropertyDescriptor類:表示一個JavaBean屬性的信息,包括getter和setter方法。
以下是一個簡單的示例,我們首先獲取User的JavaBean信息BeanInfo,然后找到屬性描述符PropertyDescriptor列表。
首先定義一個JavaBean:
@Data
public static class User {
private String username;
}
這里借助Lombok的@Data注解,生成標(biāo)準(zhǔn)JavaBean。
首先獲取BeanInfo實例:
final BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
此時beanInfo中就可以獲取User類的所有定義:
- 通過getMethodDescriptors()可以獲取方法描述;
- 通過getPropertyDescriptors()可以獲取屬性描述;
- 使用PropertyDescriptor可以生成指定屬性的描述;
少年,至此你已經(jīng)學(xué)會屠龍技,可以開始屠龍了。
來個小李子
小李子
話說小李子年輕時候真帥,可惜歲月是把殺豬刀~~
咳咳,言歸正傳,我們用上面學(xué)到的直接開始:
final BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class);
final User user = new User();
for (MethodDescriptor methodDescriptor : userBeanInfo.getMethodDescriptors()) {
System.out.println("Method Name: " + methodDescriptor.getName());
final ParameterDescriptor[] parameterDescriptors = methodDescriptor.getParameterDescriptors();
if (parameterDescriptors == null) {
continue;
}
for (ParameterDescriptor parameterDescriptor : parameterDescriptors) {
System.out.println("Parameter Name: " + parameterDescriptor.getName());
System.out.println("Parameter DisplayName: " + parameterDescriptor.getDisplayName());
}
}
// 遍歷所有屬性描述符
for (PropertyDescriptor prop : userBeanInfo.getPropertyDescriptors()) {
System.out.println("Property Name: " + prop.getName());
// 獲取getter方法
final Method readMethod = prop.getReadMethod();
if (readMethod != null) {
Object value = readMethod.invoke(user);
System.out.println("Property Value: " + value);
}
if ("username".equals(prop.getName())) {
// 獲取setter方法
final Method writeMethod = prop.getWriteMethod();
if (writeMethod != null) {
Object value = writeMethod.invoke(user, "看山");
System.out.println("Property Value: " + value);
}
}
}
System.out.println(user);
輸出結(jié)果是:
Method Name: getClass
Method Name: wait
Method Name: notifyAll
Method Name: notify
Method Name: wait
Method Name: getUsername
Method Name: hashCode
Method Name: setUsername
Method Name: wait
Method Name: equals
Method Name: toString
Property Name: class
Property Value: class cn.howardliu.tutorials.core.reflect.IntrospectorDemo$User
Property Name: username
Property Value: null
Property Value: null
IntrospectorDemo.User(username=看山)
我們可以看到,MethodDescriptor方法描述符包含了User類的所有方法,包括父類的方法。
如果不是JavaBean呢?
我們再看一下,如果User不符合規(guī)范呢?
public static class UserNoSetter {
private String username;
public void username(String username) {
this.username = username;
}
@Override
public String toString() {
return "UserNoSetter{" +
"username='" + username + '\'' +
'}';
}
}
我們再看看上面的結(jié)果:
Method Name: getClass
Method Name: wait
Method Name: notifyAll
Method Name: notify
Method Name: wait
Method Name: hashCode
Method Name: setUsername
Method Name: wait
Method Name: equals
Method Name: toString
Method Name: username
Property Name: class
Property Value: class cn.howardliu.tutorials.core.reflect.IntrospectorDemo$UserNoSetter
UserNoSetter{username='null'}
可以看到,方法除了自定義的username和setUsername,其他都是相同的,但是setUsername我們定義成了builder模式,所以內(nèi)省描述符也不認(rèn)識了。