内省(Introspector)是与反射有关的知识,主要对JavaBean进行操作。
那么什么是JavaBean?
JavaBean是一个特殊的Java类,这个特殊Java类里面的方法的名称符合某种约定的规则。
比如要获取一个对象上的age属性,获取age属性的方法名必须要写为getAge,并且getAge方法要有返回值。
设置age属性的方法名必须要为setAge,并且setAge方法没有返回值而且还要接收一个参数。
这些方法的前面要么set打头,要么get打头,这就叫符合某种特定的规则,符合这种特定规则的Java类就称为JavaBean
class Person{
private int x;
public int getAge() {
return x;
}
public void setAge(int age) {
this.x = age;
}
}
以普通Java类来看,这个类有个x名称的成员变量。
外部的人看不到类里面的私有变量,只能看得到你的公有方法,能看到你有个公有方法叫getAge(),他认为你有个age属性。
如果把一个Java类当中JavaBean来看的话,那么这个JavaBean的属性(而不是Java类的属性)
是根据getXXX和setXXX方法的名称来推断出来的。而不是内部的成员变量的名称。
如果把Person类当做一个JavaBean来看,它有一个age名称的属性,而不是x。
如果把Person当做JavaBean来操作的话,若要设置属性,设置的是age这个属性。不能说是设置x。因为我们根本就看不到x。
因此JavaBean的属性是根据方法名称来的。
------------------一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
1、在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
2、JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?
用内省这套api操作JavaBean比用普通类的方式更方便。
------------------
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
setId()的属性名->id
isLast()的属性名->last
setCPU的属性名是什么?->CPU
getUPS的属性名是什么?->UPS
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
-----------------------
一个简单的JavaBean
public class Person { //这么一个类就可以称为javaBean; 用来封装数据的
private String name; //字段对外提供了get或set方法。这个字段才能称之为属性。
/*一个Bean的属性不由它的字段决定,而由它的set或get方法决定
* 一个类有几个set或get方法它就有几个属性。
*/
private String password;
private int age;
/**
* Object类里有个getClass方法。所以getClass也是Object的属性。
* 所有类都是从Object继承而来。所以getClass也Person的属性。
*/
/*
public String getABC(){ //是属性,程序运行结果有ABC属性
return null;
}*/
public void setABC(char age){//setABC()方法里要带有参数,不然ABC不是属性、程序运行结果没有ABC属性。
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
-------------------
反射:一个类有多个组成部分,例如成员变量,方法,构造方法等。反射就是加载类,并解剖出类的各个组成部分。
为什么需要内省?(Introspector)
开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套用于操作JavaBean的API,专门用于操作java对象的属性。
访问JavaBean属性的两种方式:
1、直接调用bean的setXXX或getXXX方法。
2、通过内省技术访问(java.beans包提供了内省的API),内省技术访问也提供了两种方式。
(1)、通过PropertyDescriptor类操作Bean的属性。
(2)、通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
--------------------------
PropertyDescriptor属性描述器
-
public class PropertyDescriptor
- extends FeatureDescriptor
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。
构造方法摘要 | |
---|---|
PropertyDescriptor(String propertyName,Class<?> beanClass) 通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。 |
|
PropertyDescriptor(String propertyName,Class<?> beanClass,String readMethodName,String writeMethodName) 此构造方法带有一个简单属性的名称和用于读写属性的方法名称。 |
|
PropertyDescriptor(String propertyName,Method readMethod,Method writeMethod) 此构造方法带有某一简单属性的名称,以及用来读取和写入属性的 Method 对象。 |
方法摘要 |
---|
getReadMethod() 获得应该用于读取属性值的方法。 |
|
getWriteMethod() 获得应该用于写入属性值的方法。 |
---------------------------
使用内省的方式对JavaBean进行操作:
需求:用内省的方式来读取JavaBean对象的x属性。
JavaBean:
public class Point {
private int x;
private int y;
Point(int x,int y){
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
IntrospectorTest:
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class IntrospectorTest {
public static void main(String[] args) throws Exception {
Point p = new Point(3,4);
String propertyName = "x";
//PropertyDescriptor构造方法的参数:第一个参数是属性名;第二个参数是要把哪一个Java类当中JavaBean来看
//从JavaBean身上获取属性,pd就代表了JavaBean的属性。
PropertyDescriptor pd = new PropertyDescriptor(propertyName, p.getClass());
//JavaBean的属性是由setXXX和getXXX组合出来的。既然得到了JavaBean的属性,那么进一步就可以得到属性背后的get和set方法
Method methodGetX = pd.getReadMethod();// getReadMethod是得到属性的只读的方法getXXX;
Object retVal = methodGetX.invoke(p);//在p对象身上调用get方法,get方法不接收任何参数。
System.out.println(retVal); //3
//取出x的值还是原来反射的那一套,但是简便了很多
// 如果要把一个类当中JavaBean来操作,要取某个属性,一定是通过get方法来取。而要得到属性名对应的get方法是很麻烦的。"x"-->"X"-->"getX"-->methodGetX
//而使用内省的API得到属性的get方法是很简单的,只要把JavaBean和属性名传进去就可以了。就会得到属性对应的get和set方法
Method methodSetX = pd.getWriteMethod();//getWriteMethod()是得到属性的只写的方法setXXX;
methodSetX.invoke(p,7);//在p对象身上调用set方法,set方法接收一个参数。
retVal = methodGetX.invoke(p);
System.out.println(retVal); //7
}
}
-------------------
对JavaBean的复杂内省操作
内省的入口类(核心类)为:Introspector
-
public class Introspector
- extends Object
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
对于这三种信息,Introspector 将分别分析 bean 的类和超类,寻找显式或隐式信息,使用这些信息构建一个全面描述目标 bean 的 BeanInfo 对象。
方法摘要 |
---|
static BeanInfo |
getBeanInfo(Class<?> beanClass) 在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。 |
static BeanInfo |
getBeanInfo(Class<?> beanClass, Class<?> stopClass) 在给定的“断”点之下,在 Java Bean 上进行内省,了解其所有属性和公开的方法。 |
getBeanInfo
public static BeanInfo getBeanInfo(Class<?> beanClass)
throws IntrospectionException
-
在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
如果 Java Bean 的 BeanInfo 类以前已经被内省,则从 BeanInfo 缓存中检索 BeanInfo 类。
-
- 参数:
-
beanClass
- 将要分析的 bean 类。 - 返回:
- 描述目标 bean 的 BeanInfo 对象。
- 抛出:
-
IntrospectionException
- 如果在内省期间发生异常。 - 另请参见:
-
flushCaches()
,flushFromCaches(java.lang.Class)
getBeanInfo
public static BeanInfo getBeanInfo(Class<?> beanClass,
Class<?> stopClass)
throws IntrospectionException
-
在给定的“断”点之下,在 Java Bean 上进行内省,了解其所有属性和公开的方法。
如果 Java Bean 的 BeanInfo 类以前已经基于相同的参数被内省,则从 BeanInfo 缓存中检索 BeanInfo 类。
-
- 参数:
-
beanClass
- 将要分析的 bean 类。 -
stopClass
- 从其所在位置开始停止分析的基类。stopClass 或其基类中的所有方法/属性/事件都将在分析中被忽略。 - 抛出:
-
IntrospectionException
- 如果在内省期间发生异常。
用这个类到底Introspector 省谁?
可以调用它的getBeanInfo(beanClass)。给进去的bean是哪个就省谁。这个方法内部就会对这个Bean内省。省出来后就会把Bean的所有属性封装到一个BeanInfo的对象中(getBeanInfo方法负责返回BeanInfo接口的实例)。拿到这个对象就相当于拿到Bean的所有属性了。接着调用它的 getPropertyDescriptors() 方法就可以拿到每个属性的属性描述器(保存到PropertyDescriptor[] 数组中)。拿到描述器后就可以调用描述器的getReadMethod()方法(获得应该用于读取属性值的方法)得到属性的读方法即得到属性的get方法,就可以get这个属性的值。getWriteMethod()(获得应该用于写入属性值的方法)得到属性的写方法即得到属性的set方法,就可以往属性上设置值。
例子:
JavaBean:package com.congwiny.introspector;
public class Person { //这么一个类就可以称为javaBean; 用来封装数据的
private String name; //字段对外提供了get或set方法。这个字段才能称之为属性。
/*一个Bean的属性不由它的字段决定,而由它的set或get方法决定
* 一个类有几个set或get方法它就有几个属性。
*/
private String password;
private int age;
/**
* Object类里有个getClass方法。所以getClass也是Object的属性。
* 所有类都是从Object继承而来。所以getClass也Person的属性。
*/
/*
public String getABC(){ //是属性,程序运行结果有ABC属性
return null;
}*/
public void setABC(char age){//setABC()方法里要带有参数,不然ABC不是属性、程序运行结果没有ABC属性。
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试类Demo1:
package com.congwiny.introspector;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.junit.Test;
//使用内省API操作bean属性
public class Demo1 {
@Test
public void test1() throws Exception{
//得到所有的属性
//BeanInfo info = Introspector.getBeanInfo(Person.class);
//得到bean自己的属性
BeanInfo info = Introspector.getBeanInfo(Person.class,Object.class);//砍掉(stop掉)从父类Object继承的属性
PropertyDescriptor[] pds = info.getPropertyDescriptors();//得到所有的属性描述
for(PropertyDescriptor pd : pds){
System.out.println(pd.getName()); //输出类Person所有属性的名字
}
}
//操纵bean的指定属性:age
@Test
public void test2() throws Exception{
Person p = new Person();
PropertyDescriptor pd = new PropertyDescriptor("age", Person.class);
//得到属性的些方法,为属性赋值
Method method = pd.getWriteMethod(); //public void setAge(int age)
method.invoke(p, 45);
//获取属性的值
method = pd.getReadMethod();
System.out.println(method.invoke(p, null));
}
//高级点内容:获取当前操作的属性的类型
@Test
public void test3() throws Exception{
Person p = new Person();
PropertyDescriptor pd = new PropertyDescriptor("age", Person.class);
System.out.println(pd.getPropertyType());
}
}