Java 內(nèi)省(Introspector)深入理解
Java 內(nèi)省(Introspector)深入理解
一些概念:
內(nèi)省(Introspector) 是Java 語言對(duì) JavaBean 類屬性、事件的一種缺省處理方法。
JavaBean是一種特殊的類,主要用于傳遞數(shù)據(jù)信息,這種類中的方法主要用于訪問私有的字段,且方法名符合某種命名規(guī)則。如果在兩個(gè)模塊之間傳遞信息,可以將信息封裝進(jìn)JavaBean中,這種對(duì)象稱為“值對(duì)象”(Value Object),或“VO”。方法比較少。這些信息儲(chǔ)存在類的私有變量中,通過set()、get()獲得。
例如類UserInfo :
package com.peidasoft.Introspector;
public class UserInfo {
private long userId;
private String userName;
private int age;
private String emailAddress;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}
在類UserInfo中有屬性 userName, 那我們可以通過 getUserName,setUserName來得到其值或者設(shè)置新的值。通過 getUserName/setUserName來訪問 userName屬性,這就是默認(rèn)的規(guī)則。 Java JDK中提供了一套 API 用來訪問某個(gè)屬性的 getter/setter 方法,這就是內(nèi)省。
JDK內(nèi)省類庫:
PropertyDescriptor類:
PropertyDescriptor類表示JavaBean類通過存儲(chǔ)器導(dǎo)出一個(gè)屬性。主要方法:
1. getPropertyType(),獲得屬性的Class對(duì)象;
2. getReadMethod(),獲得用于讀取屬性值的方法;getWriteMethod(),獲得用于寫入屬性值的方法;
3. hashCode(),獲取對(duì)象的哈希值;
4. setReadMethod(Method readMethod),設(shè)置用于讀取屬性值的方法;
5. setWriteMethod(Method writeMethod),設(shè)置用于寫入屬性值的方法。
實(shí)例代碼如下:
package com.peidasoft.Introspector;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class BeanInfoUtil {
public static void setProperty(UserInfo userInfo,String userName)throws Exception{
PropertyDescriptor propDesc=new PropertyDescriptor(userName,UserInfo.class);
Method methodSetUserName=propDesc.getWriteMethod();
methodSetUserName.invoke(userInfo, "wong");
System.out.println("set userName:"+userInfo.getUserName());
}
public static void getProperty(UserInfo userInfo,String userName)throws Exception{
PropertyDescriptor proDescriptor =new PropertyDescriptor(userName,UserInfo.class);
Method methodGetUserName=proDescriptor.getReadMethod();
Object objUserName=methodGetUserName.invoke(userInfo);
System.out.println("get userName:"+objUserName.toString());
}
}
Introspector類:
將JavaBean中的屬性封裝起來進(jìn)行操作。在程序把一個(gè)類當(dāng)做JavaBean來看,就是調(diào)用Introspector.getBeanInfo()方法,得到的BeanInfo對(duì)象封裝了把這個(gè)類當(dāng)做JavaBean看的結(jié)果信息,即屬性的信息。
getPropertyDescriptors(),獲得屬性的描述,可以采用遍歷BeanInfo的方法,來查找、設(shè)置類的屬性。具體代碼如下:
package com.peidasoft.Introspector;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class BeanInfoUtil {
public static void setPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
if(proDescrtptors!=null&&proDescrtptors.length>0){
for(PropertyDescriptor propDesc:proDescrtptors){
if(propDesc.getName().equals(userName)){
Method methodSetUserName=propDesc.getWriteMethod();
methodSetUserName.invoke(userInfo, "alan");
System.out.println("set userName:"+userInfo.getUserName());
break;
}
}
}
}
public static void getPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
if(proDescrtptors!=null&&proDescrtptors.length>0){
for(PropertyDescriptor propDesc:proDescrtptors){
if(propDesc.getName().equals(userName)){
Method methodGetUserName=propDesc.getReadMethod();
Object objUserName=methodGetUserName.invoke(userInfo);
System.out.println("get userName:"+objUserName.toString());
break;
}
}
}
}
}
通過這兩個(gè)類的比較可以看出,都是需要獲得PropertyDescriptor,只是方式不一樣:前者通過創(chuàng)建對(duì)象直接獲得,后者需要遍歷,所以使用PropertyDescriptor類更加方便。
使用實(shí)例:
package com.peidasoft.Introspector;
public class BeanInfoTest {
/**
* @param args
*/
public static void main(String[] args) {
UserInfo userInfo=new UserInfo();
userInfo.setUserName("peida");
try {
BeanInfoUtil.getProperty(userInfo, "userName");
BeanInfoUtil.setProperty(userInfo, "userName");
BeanInfoUtil.getProperty(userInfo, "userName");
BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");
BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
BeanInfoUtil.setProperty(userInfo, "age");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
輸出:
get userName:peida set userName:wong get userName:wong set userName:alan get userName:alan java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.peidasoft.Introspector.BeanInfoUtil.setProperty(BeanInfoUtil.java:14) at com.peidasoft.Introspector.BeanInfoTest.main(BeanInfoTest.java:22)
說明:BeanInfoUtil.setProperty(userInfo, "age");報(bào)錯(cuò)是應(yīng)為age屬性是int數(shù)據(jù)類型,而setProperty方法里面默認(rèn)給age屬性賦的值是String類型。所以會(huì)爆出argument type mismatch參數(shù)類型不匹配的錯(cuò)誤信息。
BeanUtils工具包:
由上述可看出,內(nèi)省操作非常的繁瑣,所以所以Apache開發(fā)了一套簡單、易用的API來操作Bean的屬性——BeanUtils工具包。
BeanUtils工具包:下載:http://commons.apache.org/beanutils/ 注意:應(yīng)用的時(shí)候還需要一個(gè)logging包 http://commons.apache.org/logging/
使用BeanUtils工具包完成上面的測試代碼:
package com.peidasoft.Beanutil;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import com.peidasoft.Introspector.UserInfo;
public class BeanUtilTest {
public static void main(String[] args) {
UserInfo userInfo=new UserInfo();
try {
BeanUtils.setProperty(userInfo, "userName", "peida");
System.out.println("set userName:"+userInfo.getUserName());
System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
BeanUtils.setProperty(userInfo, "age", 18);
System.out.println("set age:"+userInfo.getAge());
System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
PropertyUtils.setProperty(userInfo, "age", 8);
System.out.println(PropertyUtils.getProperty(userInfo, "age"));
System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
PropertyUtils.setProperty(userInfo, "age", "8");
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果:
set userName:peida get userName:peida set age:18 get age:18 get userName type:java.lang.String get age type:java.lang.String 8 java.lang.Integer Exception in thread "main" java.lang.IllegalArgumentException: Cannot invoke com.peidasoft.Introspector.UserInfo.setAge on bean class 'class com.peidasoft.Introspector.UserInfo' - argument type mismatch - had objects of type "java.lang.String" but expected signature "int" at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2235) at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:2151) at org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1957) at org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:2064) at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:858) at com.peidasoft.orm.Beanutil.BeanUtilTest.main(BeanUtilTest.java:38) Caused by: java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2170) ... 5 more
說明:
1.獲得屬性的值,例如,BeanUtils.getProperty(userInfo,"userName"),返回字符串
2.設(shè)置屬性的值,例如,BeanUtils.setProperty(userInfo,"age",8),參數(shù)是字符串或基本類型自動(dòng)包裝。設(shè)置屬性的值是字符串,獲得的值也是字符串,不是基本類型?! ?.BeanUtils的特點(diǎn):
1). 對(duì)基本數(shù)據(jù)類型的屬性的操作:在WEB開發(fā)、使用中,錄入和顯示時(shí),值會(huì)被轉(zhuǎn)換成字符串,但底層運(yùn)算用的是基本類型,這些類型轉(zhuǎn)到動(dòng)作由BeanUtils自動(dòng)完成。
2). 對(duì)引用數(shù)據(jù)類型的屬性的操作:首先在類中必須有對(duì)象,不能是null,例如,private Date birthday=new Date();。操作的是對(duì)象的屬性而不是整個(gè)對(duì)象,例如,BeanUtils.setProperty(userInfo,"birthday.time",111111);
package com.peidasoft.Introspector;
import java.util.Date;
public class UserInfo {
private Date birthday = new Date();
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Date getBirthday() {
return birthday;
}
}
package com.peidasoft.Beanutil;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.beanutils.BeanUtils;
import com.peidasoft.Introspector.UserInfo;
public class BeanUtilTest {
public static void main(String[] args) {
UserInfo userInfo=new UserInfo();
try {
BeanUtils.setProperty(userInfo, "birthday.time","111111");
Object obj = BeanUtils.getProperty(userInfo, "birthday.time");
System.out.println(obj);
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
3.PropertyUtils類和BeanUtils不同在于,運(yùn)行g(shù)etProperty、setProperty操作時(shí),沒有類型轉(zhuǎn)換,使用屬性的原有類型或者包裝類。由于age屬性的數(shù)據(jù)類型是int,所以方法PropertyUtils.setProperty(userInfo, "age", "8")會(huì)爆出數(shù)據(jù)類型不匹配,無法將值賦給屬性。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
SpringCloud實(shí)現(xiàn)基于RabbitMQ消息隊(duì)列的詳細(xì)步驟
在Spring Cloud框架中,我們可以利用RabbitMQ實(shí)現(xiàn)強(qiáng)大而可靠的消息隊(duì)列系統(tǒng),本篇將詳細(xì)介紹如何在Spring Cloud項(xiàng)目中集成RabbitMQ,并創(chuàng)建一個(gè)簡單的消息隊(duì)列,感興趣的朋友一起看看吧2024-03-03
java中for循環(huán)執(zhí)行的順序圖文詳析
關(guān)于java的for循環(huán)想必大家非常熟悉,它是java常用的語句之一,這篇文章主要給大家介紹了關(guān)于java中for循環(huán)執(zhí)行順序的相關(guān)資料,需要的朋友可以參考下2021-06-06
如何使用Spring Security手動(dòng)驗(yàn)證用戶的方法示例
這篇文章主要介紹了如何使用Spring Security手動(dòng)驗(yàn)證用戶的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05
Elasticsearch?mapping?概念及自動(dòng)創(chuàng)建示例
這篇文章主要為大家介紹了Elasticsearch?mapping?概念及自動(dòng)創(chuàng)建示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

