做了这么久Java程序开发,反射(Reflect)的概念倒是知道了不少,可是还有一种方式是跟反射有紧密联系的,我们平时很少谈及到,这就是内省(Introspector)了。
那什么是内省呢?
内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。
Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则,这些 API 存放于包 java.beans 中,一般的做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
在讲解内省的使用之前,我们首先要了解一个概念,就是一个JavaBean中怎么计算他有多少个属性。
在java语法里面,一个JavaBean的属性不是看他有多少个字段,而是看他的getter和setter方法的数量,我们在User类里面设置了多个字段,代码如下:
但是如果我们不为这些字段设置任何setter和getter方法的话,java语言默认为该类没有任何属性(其实还有一个属性,是继承于Object类的Class属性,因为Object为Class属性设置了getClass()的方法。)
相反,如果User类没有一个字段,但是却有很多getter和setter方法,那么java会计算这些getter和setter的数量来确定有多少属性(一般getter和setter方法是成对的,所以一对算一个属性,但是也可以只存在getter或者setter单个方法,这样单个也算一个属性),我们来看下面的这个叫做Dinner的JavaBean。
我们可以看到,这个bean里面有个字段rice,但是rice是没有getter和setter方法的,所以不能算Dinner类的属性,egg和milk既有getter方法也有setter方法。而bread只有setter方法,Cake只有get方法,他们都算Dinner类的属性。
这里面还有个很特别的Water,是私有的方法,也有一个相应的字段叫water(这里大家很容易混淆,但是私有方法再神似也不能叫getter和setter方法,所以water不能算Dinner类的属性)。最后我们运行内省来查看所有属性名称,结果如下:
[quote]result:
bread
cake
egg
milk
[/quote]
可见,egg milk虽然没有字段,但是setter和getter方法都有,所以属性描述器就认为他们是Dinner类的属性,bread只有setter方法,而cake只有getter方法,但是属性描述器也认为他们两个都是Dinner类的属性。
现在让我们来看以下的代码,以便于我们更容易的了解内省的使用方法。他跟反射的使用方法有异曲同工的作用。
那什么是内省呢?
内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。
Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则,这些 API 存放于包 java.beans 中,一般的做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
在讲解内省的使用之前,我们首先要了解一个概念,就是一个JavaBean中怎么计算他有多少个属性。
在java语法里面,一个JavaBean的属性不是看他有多少个字段,而是看他的getter和setter方法的数量,我们在User类里面设置了多个字段,代码如下:
private String userName;
private String password;
private int age;
private boolean isFemale;
但是如果我们不为这些字段设置任何setter和getter方法的话,java语言默认为该类没有任何属性(其实还有一个属性,是继承于Object类的Class属性,因为Object为Class属性设置了getClass()的方法。)
相反,如果User类没有一个字段,但是却有很多getter和setter方法,那么java会计算这些getter和setter的数量来确定有多少属性(一般getter和setter方法是成对的,所以一对算一个属性,但是也可以只存在getter或者setter单个方法,这样单个也算一个属性),我们来看下面的这个叫做Dinner的JavaBean。
public class Dinner {
private String rice;
private String water;
public String getEgg() {
return "egg";
}
public void setEgg(String egg) {}
public String getMilk() {
return "milk";
}
public void setMilk(String milk) {}
public void setBread(String bread) {}
public String getCake() {
return "cake";
}
private String getWater() {
return "water";
}
private void setWater(String water) {}
}
我们可以看到,这个bean里面有个字段rice,但是rice是没有getter和setter方法的,所以不能算Dinner类的属性,egg和milk既有getter方法也有setter方法。而bread只有setter方法,Cake只有get方法,他们都算Dinner类的属性。
这里面还有个很特别的Water,是私有的方法,也有一个相应的字段叫water(这里大家很容易混淆,但是私有方法再神似也不能叫getter和setter方法,所以water不能算Dinner类的属性)。最后我们运行内省来查看所有属性名称,结果如下:
//这段代码就是使用内省来获得一个类里面的所有属性的,稍后我们会具体介绍。
public void findDinnerProperties() throws IntrospectionException{
//通过Introspector获取一个类的BeanInfo,这个BeanInfo就是该类所有属性的集合
BeanInfo info = Introspector.getBeanInfo(Dinner.class, Object.class);
//通过BeanInfo获取PropertyDescriptor,就是属性描述器,描述具体每个属性
PropertyDescriptor[] pds = info.getPropertyDescriptors();
//foreach遍历获得每个属性的名字。
for (PropertyDescriptor pd : pds) {
System.out.println(pd.getName());
}
}
[quote]result:
bread
cake
egg
milk
[/quote]
可见,egg milk虽然没有字段,但是setter和getter方法都有,所以属性描述器就认为他们是Dinner类的属性,bread只有setter方法,而cake只有getter方法,但是属性描述器也认为他们两个都是Dinner类的属性。
现在让我们来看以下的代码,以便于我们更容易的了解内省的使用方法。他跟反射的使用方法有异曲同工的作用。
//让我们在第一个方法中了解到以上我们所说的获取BeanInfo和PropertyDescriptor的步骤
@Test
public void useInspector() throws IntrospectionException{
//通过Introspector获取一个类的BeanInfo,这个BeanInfo就是该类所有属性的集合,本来只需要一个参数,就是User类的class,第二个参数说明需要排除从Object类中继承而来的所有属性。
BeanInfo info = Introspector.getBeanInfo(User.class, Object.class);
//通过BeanInfo获取PropertyDescriptor,就是属性描述器,描述具体每个属性
PropertyDescriptor[] pds = info.getPropertyDescriptors();
//foreach遍历获得每个属性的名字。
for (PropertyDescriptor pd : pds) {
System.out.println(pd.getName());
}
}
//第二个方法是使用内省来获取getter和setter方法的案例。当我们获得了getter和setter方法后,表示我们已经能够正常获得方法对应的属性了。
@Test
public void usePropertyDescriptor() throws Exception{
//通过反射获得一个user对象,以便于之后使用该对象调用方法
Class clazz = Class.forName("com.ncs.tang.User");
User user = (User) clazz.newInstance();
//使用PropertyDescriptor对象来获取User类里面的一个属性field4
PropertyDescriptor pd = new PropertyDescriptor("field4", User.class);
//获得对象的写属性来调用set方法为属性赋值
Method mw = pd.getWriteMethod();
mw.invoke(user, 17.5f);
//获得对象的读属性来调用get方法获取值
Method mr = pd.getReadMethod();
System.out.println(mr.invoke(user));
//获取相应属性的类型(Class)
System.out.println(pd.getPropertyType());
}