java基础加强--JavaBean和内省Introspector

时间:2021-05-08 19:40:15

内省(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类的属性)

是根据getXXXsetXXX方法的名称来推断出来的。而不是内部的成员变量的名称。

如果把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的属性是根据其中的settergetter方法来确定的,而不是根据其中的成员变量。如果方法名为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公司开发了一套用于操作JavaBeanAPI,专门用于操作java对象的属性。

访问JavaBean属性的两种方式:

1、直接调用beansetXXXgetXXX方法。

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 对象。
方法摘要

Method

getReadMethod()          获得应该用于读取属性值的方法。

 Method

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());
}
}