内省(Introspector) 是Java 語言對JavaBean類屬性、事件的一種預設處理方法。
JavaBean是一種特殊的類,主要用于傳遞資料資訊,這種類中的方法主要用于通路私有的字段,且方法名符合某種命名規則。如果在兩個子產品之間傳遞資訊,可以将資訊封裝進JavaBean中,這種對象稱為“值對象”(Value Object),或“VO”。方法比較少。這些資訊儲存在類的私有變量中,通過set()、get()獲得。
例如類UserInfo :
package com.peidasoft.instrospector;
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來得到其值或者設定新的值。通過getUserName/setUserName來通路userName屬性,這就是預設的規則。Java JDK中提供了一套API用來通路某個屬性的getter/setter方法,這就是内省。
JDK内省類庫:
PropertyDescriptor類:(屬性描述器)
PropertyDescriptor類表示JavaBean類通過存儲器導出一個屬性。主要方法:
1. getPropertyType(),獲得屬性的Class對象;
2. getReadMethod(),獲得用于讀取屬性值的方法;
3. getWriteMethod(),獲得用于寫入屬性值的方法;
4. hashCode(),擷取對象的哈希值;
5. setReadMethod(Method readMethod),設定用于讀取屬性值的方法;
6. setWriteMethod(Method writeMethod),設定用于寫入屬性值的方法。
執行個體代碼如下:
package com.peidasoft.instrospector;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class BeanInfoUtil {
// 設定bean的某個屬性值
public static void setProperty(UserInfo userInfo, String userName) throws Exception {
// 擷取bean的某個屬性的描述符
PropertyDescriptor propDesc = new PropertyDescriptor(userName, UserInfo.class);
// 獲得用于寫入屬性值的方法
Method methodSetUserName = propDesc.getWriteMethod();
// 寫入屬性值
methodSetUserName.invoke(userInfo, "wong");
System.out.println("set userName:" + userInfo.getUserName());
}
// 擷取bean的某個屬性值
public static void getProperty(UserInfo userInfo, String userName) throws Exception {
// 擷取Bean的某個屬性的描述符
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中的屬性封裝起來進行操作。在程式把一個類當做JavaBean來看,就是調用Introspector.getBeanInfo()方法,得到的BeanInfo對象封裝了把這個類當做JavaBean看的結果資訊,即屬性的資訊。
getPropertyDescriptors(),獲得屬性的描述,可以采用周遊BeanInfo的方法,來查找、設定類的屬性。具體代碼如下:
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class BeanInfoUtil {
// 通過内省設定bean的某個屬性值
public static void setPropertyByIntrospector(UserInfo userInfo, String userName) throws Exception {
// 擷取bean資訊
BeanInfo beanInfo = Introspector.getBeanInfo(UserInfo.class);
// 擷取bean的所有屬性清單
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;
}
}
}
}
// 通過内省擷取bean的某個屬性值
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;
}
}
}
}
}
通過這兩個類的比較可以看出,都是需要獲得PropertyDescriptor,隻是方式不一樣:前者通過建立對象直接獲得,後者需要周遊,是以使用PropertyDescriptor類更加友善。
package com.peidasoft.instrospector;
public class BeanInfoTest {
/**
* @param args the command line arguments
*/
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"); // IllegalArgumentException
} catch (Exception e) {
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:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.peidasoft.instrospector.BeanInfoUtil.setProperty(BeanInfoUtil.java:22)
at com.peidasoft.instrospector.BeanInfoTest.main(BeanInfoTest.java:26)
說明:BeanInfoUtil.setProperty(userInfo,"age");報錯是應為age屬性是int資料類型,而setProperty方法裡面預設給age屬性賦的值是String類型。是以會爆出argument type mismatch參數類型不比對的錯誤資訊。
BeanUtils工具包:
由上述可看出,内省操作非常的繁瑣,是以是以Apache開發了一套簡單、易用的API來操作Bean的屬性——BeanUtils工具包。
BeanUtils工具包:下載下傳:http://commons.apache.org/beanutils/,注意:應用的時候還需要一個logging包http://commons.apache.org/logging/
使用BeanUtils工具包完成上面的測試代碼:
package com.peidasoft.instrospector;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
public class BeanInfoTest {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setUserName("peida");
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"); // IllegalArgumentException
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
運作結果:
[java] view plain copy
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.instrospector.UserInfo.setAge on bean class
'class com.peidasoft.instrospector.UserInfo' - argument type mismatch - had objects of type "java.lang.String" but expected signature "int"
at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2181)
at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:2097)
at org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1903)
at org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:2010)
at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:896)
at com.peidasoft.instrospector.BeanInfoTest.main(BeanInfoTest.java:32)
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2116)
... 5 more
說明:
1. 獲得屬性的值,例如,BeanUtils.getProperty(userInfo, "userName"),傳回字元串。
2. 設定屬性的值,例如,BeanUtils.setProperty(userInfo, "age", 8),參數是字元串或基本類型自動包裝。設定屬性的值是字元串,獲得的值也是字元串,不是基本類型。 3. BeanUtils的特點:
1). 對基本資料類型的屬性的操作:在WEB開發、使用中,錄入和顯示時,值會被轉換成字元串,但底層運算用的是基本類型,這些類型轉到動作由BeanUtils自動完成。
2). 對引用資料類型的屬性的操作:首先在類中必須有對象,不能是null,例如,private Date birthday=new Date();。操作的是對象的屬性而不是整個對象,例如,BeanUtils.setProperty(userInfo, "birthday.time", 111111);
package com.peidasoft.Introspector;
import java.util.Date;
public class UserInfo {
private Date birthday = new Date(); // 引用類型的屬性,不能為null
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不同在于,運作getProperty、setProperty操作時,沒有類型轉換,使用屬性的原有類型或者包裝類。由于age屬性的資料類型是int,是以方法PropertyUtils.setProperty(userInfo,"age", "8")會爆出資料類型不比對,無法将值賦給屬性。
聲明本文轉自:http://blog.csdn.net/zhoudaxia/article/details/33783321
轉載于:https://www.cnblogs.com/pony1223/p/7450837.html