爲什麼要學內省?
開發框架時,經常需要使用java對象的屬性來封裝程序的數據,每次都使用反射技術完成此類操作過於麻煩,所以sun公司開發了一套API,專門用於操作java對象的屬性。
什麼是Java對象的屬性和屬性的讀寫方法?
內省訪問JavaBean屬性的兩種方式:
通過PropertyDescriptor類操作Bean的屬性;
通過Introspector類獲得Bean對象的
BeanInfo,然後通過
BeanInfo來獲取屬性的描述器(
PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的getter/setter
方法,然後通過反射機制來調用這些方法。
¨ 內省是Java
語言對Bean 類屬性的一種缺省處理方法。例如類A
中有屬性name,
可以通過getName,setName來得到其值或者設置新的值。通過
getName/setName來訪問
name屬性,這是默認的規則。
Java中提供了一套
API來訪問某個屬性的 getter/setter方法。
¨一般的做法是通過類Introspector來獲取某個對象的
BeanInfo信息,然後通過
BeanInfo來獲取屬性的描述器(
PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的
getter/setter方法,然後通過反射機制來調用這些方法。
下面,通過一段代碼,大家可以看看利用BeanInfo和Inspector如何實現內省:
Person類:
public class Person {
private String name;
public int age;
// public String xxx;
// 定義set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
System.out.println("-----目標方法------");
return age;
}
public void setAge(int age) {
this.age = age;
}
/*
* public void setXxx(String xxx){ this.xxx=xxx; }
*/
//在這裏,只要滿足java定義的標準,都回算是一個對象,所以在測試類中,獲取所有屬性的PropertyDesciptor的數組長度爲getClass()+所有滿足java定義標準的get()方法的個數
public String getXxx() {
return "";
}
}
測試類:
public class Demo {
@Test
public void test() throws Exception {
// 1.獲取beanInfo信息
BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
// 返回一個BeanInfo信息()
// 2.獲取所有屬性的PropertyDesciptor
PropertyDescriptor pd[] = beanInfo.getPropertyDescriptors();
// getClass
System.out.println(pd.length);
// 遍歷
for (PropertyDescriptor p : pd) {
// System.out.println(p.toString());
Method m1 = p.getReadMethod();// 獲得應該用於讀取屬性值的方法
System.out.println(m1.getName());// 獲得應該用於寫入屬性值的方法
Method m2 = p.getWriteMethod();
if (m2 != null) {
System.out.println(m2.getName());
}
}
}
// 通過內省機制設置age字段的值
@Test
public void test1() throws Exception {
// 得到操作的class對象
Class c = Person.class;
// 得到對象實例
Person p = (Person) c.newInstance();
// 得到class對象中的age字段的描述器對象
PropertyDescriptor pd = new PropertyDescriptor("age", c);
// 獲得此特性的本地化顯示名稱
// System.out.println(pd.getDisplayName());
// 得到age字段操作的寫入方法對象
Method m = pd.getWriteMethod();
// 去執行目標p對象的目標方法(即pd.getWriteMethod()指向的方法),併爲其age設置10的值
m.invoke(p, 10);// 設置值
// 查看是否成功
System.out.println(p.getAge());
}
// 通過內省機制讀取age字段的值
@Test
public void test2() throws Exception {
// 得到操作的class對象
Class c = Person.class;
// 得到對象實例
Person p = (Person) c.newInstance();
p.setAge(100);// 設置值
// 得到class對象中的age字段的描述器對象
PropertyDescriptor pd = new PropertyDescriptor("age", c);
// 獲得此特性的本地化顯示名稱
// System.out.println(pd.getDisplayName());
// 得到age字段操作的讀取方法對象
Method m = pd.getReadMethod();
// 去執行目標p對象的目標方法(即pd.getReadMethod()指向的方法),併爲其age設置10的值
System.out.println("在執行目標方法之前所做的事情----");
Object value = m.invoke(p);// 設置值
// 查看是否成功
System.out.println(value);
System.out.println("在執行目標方法之後所做的事情----");
}
}
在這裏我同樣用了JUnit測試內省的實現,大家可以自己測試一下,感受一下效果。
看完代碼後,個人感覺還是十分繁瑣的。不過,總有化繁爲簡的方法,Sun公司的內省API過於繁瑣,所以Apache組織結合很多實際開發中的應用場景開發了一套簡單、易用的API操作Bean的屬性——BeanUtils。
那麼如何利用這個API來實現內省的實現呢?
因爲是人家公司封裝的API,所以還是需要外界的jar包來實現此操作,這時我們就需要去相關網站去下載所需要的jar文件。下載下來後,解壓找到下圖所示的兩個jar文件,添加到類庫中。
細心的朋友也許會問到,我們需要的是BeanUtils這個Bean屬性,那麼下面的那個logging是幹嘛的呢?
先看一下的兩段代碼:
Person類:
public class Person {
private String name;
public int 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;
}
}
測試類:public class Demo {
@Test
public void test() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
Person p=new Person();//實例化Bean對象
p.setName("Retror");//設置值
String value=BeanUtils.getProperty(p, "name");//獲取指定Bean對象的制定屬性值
System.out.println(value);
}
}
如果我們沒有配置下面的那個jar文件,那麼在測試的時候就會出現如下異常錯誤:
/*java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.apache.commons.beanutils.ConvertUtilsBean.<init>(ConvertUtilsBean.java:157)
at org.apache.commons.beanutils.BeanUtilsBean.<init>(BeanUtilsBean.java:117)
at org.apache.commons.beanutils.BeanUtilsBean$1.initialValue(BeanUtilsBean.java:68)
at org.apache.commons.beanutils.ContextClassLoaderLocal.get(ContextClassLoaderLocal.java:153)
at org.apache.commons.beanutils.BeanUtilsBean.getInstance(BeanUtilsBean.java:80)
at org.apache.commons.beanutils.BeanUtils.getProperty(BeanUtils.java:382)
at www.csdn.net.beanutils.Demo.test(Demo.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
... 30 more*/
這是因爲,這個屬性就是和logging這個日誌記錄一起的,如果沒有這個記錄就會出現錯誤。