为什么要学内省?
开发框架时,经常需要使用
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这个日志记录一起的,如果没有这个记录就会出现错误。