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这个日志记录一起的,如果没有这个记录就会出现错误。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章