Java內省的使用

  爲什麼要學內省?
         開發框架時,經常需要使用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這個日誌記錄一起的,如果沒有這個記錄就會出現錯誤。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章