Java总结(17)内省javaBean


内省、JavaBean

内省 

内省(Introspector)是Java 语言通过反射的原理对  JavaBean 类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过 getName,setName 方法来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。Java中提供了一套 API 用来访问通过字符串变量传递名字的某个属性的 getter/setter 方法,通过这些 API  对对象属性的操作,减少了代码的数量。这些 API 存放于包java.beans中。

JavaBean

JavaBean 是一个非常简单的遵循某种严格协议的 Java 类,为写成 JavaBean,类必须是具体(非 abstract )的和公共( public )的,并且具有无参数的构造器。JavaBean 严格遵守面向对象的类设计逻辑,不让外部世界访问其任何字段(没有 public 字段)。这样,方法调用是接触 Bean 的唯一途径。每个JavaBean 属性通常都应该遵循简单的方法命名规则,这样应用程序构造器工具和最终用户才能找到 JavaBean 提供的属性,然后查询或修改属性值,对 bean 进行操作。

JavaBean 是一种特殊的 java 类,主要用于传递数据信息,这种 java 类中的方法主要用于访问私有的字段,且这种方法名符合某种命名规则。如果要在两个模块之间传递多个信息,可以将这些信息封装到一个 JavaBean 中,这种 JavaBean 的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,这就是 getter 和 setter 方法,JavaBean 的属性是根据其中的 setter 和 getter 方法来确定的,而不是根据其中的成员变量。如果方法名为 setId,中文意思即为设置 id,至于你把它存到哪个变量上,不用管,如果方法名为 getId,中文意思即为获取 id,至于你从哪个变量上取,不用管,去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。我们在使用 javabean 的时候,就是通过这个属性名使用 reflection method 来获取 getter setter 方法,再通过 getter setter 方法来操作 javabean 对象的属性。
-->setId()的属性名是id
-->getLast()的属性名是last
-->setCPU的属性名是CPU
-->getUPS的属性名是UPS
总之:
一个类被当作 javaBean 使用时,JavaBean 的属性是根据方法名推断出来的,它根本看不到 java 类内部的成员变量。
 一个符合 JavaBean 特点的类可以当作普通类一样进行使用,也可以当 JavaBean 使用,把它当 JavaBean 用好处如下:
1.在 Java EE 开发中,经常要使用到 JavaBean。很多环境就要求按 JavaBean 方式进行操作。
2.JDK 中提供了对 JavaBean 进行操作的一些 API,这套 API 就称为内省。如果在写程序的时候,你只知道要访问的属性名x保存在某个字符串变量 xstring 中,要通过 getX 方法来访问私有的 x,需要自己根据属性名来组合为 getter setter 方法名,然后再使用反射来访问这些方法,比较麻烦,所以JDK 提供了内省这套 API 操作JavaBean 比用普通类的方式更方便。
比如:
/** 
 * 需求:自己模拟内省的操作 
 */  
package cn.itcast.reflect;  
  
import java.lang.reflect.Field;  
import java.lang.reflect.Method;  
  
public class ReflectionCopyObject {  
  
    public Object copy(Object object) throws Exception {  
        // 获得对象的类型  
        Class<?> classType = object.getClass();  
        System.out.println("Class:" + classType.getName());  
  
        // 通过默认构造方法创建一个新的对象  
        Object objectCopy = classType.getConstructor(new Class[] {})  
                .newInstance(new Object[] {});  
  
        // 获得对象的所有属性  
        Field fields[] = classType.getDeclaredFields();  
  
        for (int i = 0; i < fields.length; i++) {  
            Field field = fields[i];  
  
            String fieldName = field.getName();  
            String firstLetter = fieldName.substring(0, 1).toUpperCase();  
            // 获得和属性对应的getXXX()方法的名字  
            String getMethodName = "get" + firstLetter + fieldName.substring(1);  
            // 获得和属性对应的setXXX()方法的名字  
            String setMethodName = "set" + firstLetter + fieldName.substring(1);  
  
            // 获得和属性对应的getXXX()方法  
            Method getMethod = classType.getMethod(getMethodName,  
                    new Class[] {});  
            // 获得和属性对应的setXXX()方法  
            Method setMethod = classType.getMethod(setMethodName,  
                    new Class[] { field.getType() });  
  
            // 调用原对象的getXXX()方法  
            Object value = getMethod.invoke(object, new Object[] {});  
            System.out.println(fieldName + ":" + value);  
            field.setAccessible(true);  
            System.out.println(field.get(object).getClass());  
            // 调用拷贝对象的setXXX()方法  
            setMethod.invoke(objectCopy, new Object[] { value });  
        }  
        return objectCopy;  
          
    }  
    public static void main(String[] args) throws Exception {  
        // TODO Auto-generated method stub  
        Customer customer = new Customer("Tom", 21);  
        customer.setId(new Long(1));  
  
        Customer customerCopy = (Customer) new ReflectionCopyObject().copy(customer);  
        System.out.println("Copy information:" + customerCopy.getId() + " "  
                + customerCopy.getName() + " " + customerCopy.getAge());  
    }  
  
}  
  
class Customer {  
    private Long id;  
  
    private String name;  
  
    private int age;  
  
    public Customer() {  
    }  
  
    public Customer(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    public Long getId() {  
        return id;  
    }  
  
    public void setId(Long id) {  
        this.id = id;  
    }  
  
    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;  
    }  
}  

对 JavaBean 的内省操作

内省访问JavaBean有两种方法:
一、通过属性描述符 PropertyDescriptor 来操作 Bean 对象
二、通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取所以属性的描述器( PropertyDescriptor ),通过某个属性描述器就可以获取这个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。

PropertyDescriptor
java.lang.Object
      java.beans.FeatureDescriptor
             java.beans.PropertyDescriptor
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性

构造器

PropertyDescriptor(String propertyName, Class<?> beanClass) 
          通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。

常用方法

Method getReadMethod() 
          获得应该用于读取属性值的方法。 
 Method getWriteMethod() 
          获得应该用于写入属性值的方法。 
Class<?> getPropertyType() 
          获得属性的 Class 对象。 
Introspector 类
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。 
java.lang.Object
       java.beans.Introspector
常用方法
static BeanInfo getBeanInfo(Class<?> beanClass) 
          在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。 


BeanInfo  接口
PropertyDescriptor[] getPropertyDescriptors() 

          获得 beans PropertyDescriptor。 

/** 
 * 需求:使用 PropertyDescriptor 进行内省操作 
 *  
 * 思路: 
 *  
 * 步骤: 
 *  
 * 总结: 
 * 1.没有 PropertyDescriptor ,我们需要自己根据属性名字符串,解析为 getter、setter 名字符串,再用 Class 对 
 * 象的 getMethod 方法获取对应的 getter、setter 方法对象 
 * 2.有 PropertyDescriptor 之后,我们只需要传递一个属性名 字符串 + Class对象,就可直接使用 getReadMethod  
 * 获取到 getter 方法对象 
 */  
package cn.itcast.reflect;  
  
import java.beans.BeanInfo;  
import java.beans.Introspector;  
import java.beans.PropertyDescriptor;  
import java.lang.reflect.Method;  
  

public class IntrospectorDemo {  

    public static void main(String[] args) throws Exception {  
        Point point = new Point(2, 5);  
        String proName = "x";  
  
        System.out.println(getProperty1(point, proName));  
        setProperty(point, proName, 8);  
        System.out.println(getProperty2(point, proName));  
    }  
  
    private static void setProperty(Point point, String proName, int value)  
            throws Exception {  
        PropertyDescriptor proDescriptor = new PropertyDescriptor(proName,  
                Point.class);  
        Method methodSetX = proDescriptor.getWriteMethod();  
        methodSetX.invoke(point, value);  
    }  
  
    private static int getProperty1(Point point, String proName)  
            throws Exception {  
        PropertyDescriptor proDescriptor = new PropertyDescriptor(proName,  
                Point.class);  
        Method methodGetX = proDescriptor.getReadMethod();  
        Object objx = methodGetX.invoke(point);  
        return Integer.parseInt(objx.toString());  
    }  
  
    private static int getProperty2(Point point, String proName)  
            throws Exception {  
        BeanInfo beanInfo = Introspector.getBeanInfo(point.getClass());  
        PropertyDescriptor[] proDescriptors = beanInfo.getPropertyDescriptors();  
        for (PropertyDescriptor prop : proDescriptors) {  
            if (prop.getName().equals(proName)) {  
                Method methodGetx = prop.getReadMethod();  
                Object objx = methodGetx.invoke(point);  
                return Integer.parseInt(objx.toString());  
            }  
        }  
        return 0;  
    }  
  
}  
  
class Point {  
    private Integer x;  
    private Integer y;  
  
    public Point() {  
  
    }  
  
    public Point(Integer x, Integer y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
  
    public Integer getX() {  
        return x;  
    }  
  
    public void setX(Integer x) {  
        this.x = x;  
    }  
  
    public Integer getY() {  
        return y;  
    }  
  
    public void setY(Integer y) {  
        this.y = y;  
    }  
} 

使用BeanUtils工具包操作JavaBean

使用 Apache 的 BeanUtils 工具包来完成对 JavaBean 的操作


使用 BeanUtils 工具包之前先需要在 project 里添加 jar 包,添加 jar 包有两种方式:

1.只是添加到 buildingpath 中,这个在本地运行,但是在其他机器上如果没有这个 jar 就不能运行,因为生成的 bin 目录下没有该 jar
2.把 jar 添加到项目中,再添加到 buildingpath 中,这个是在 building 的时候,把该 jar 一起 building 到 bin 目录中

第一种方式:在 Project properties 中的 buildingpath 下配置, Add JARs 是添加同一个 WorkSpace 中的 jar,比如其他 project 生成的 jar,Add External JARs 是添加文件系统中任意位置的 jar,这个地方只是把 jar 的路径添加到相应的配置文件中了,如果在文件系统中把对应的 jar 删掉,编译和运行时也会找不到需要的 class。

第二种方式:直接把 jar 复制到 project 中,比如在 project 中建一个 lib 目录专门用来保存 jar,然后再把该 jar 添加到 project 的 buildingpath,在 building 的时候就能把该 jar 一并添加进构建后的 jar 文件。


用 eclipse 给项目添加 jar 包,如果先导入Apache的beanutils 包,由于beanutils 包依赖于logging 包,还需要导入logging 包。
在前面内省例子的基础上,使用 Apache 的 BeanUtils 工具包来完成对 JavaBean 的操作
BeanUtils
java.lang.Object
     org.apache.commons.beanutils.BeanUtils

Utility methods for populating JavaBeans properties via reflection.

static String getProperty(Object bean, String name) 
static void setProperty(Object bean, String name, Object value) 
static void copyProperties(Object dest, Object orig) 
static Map describe(Object bean)   把 javabean 的所有属性以 map 返回,和 json 串相似 {name:"xxx",  age:'17'}
static void populate(Object bean, Map properties)  把一个 map 填充到 javabean 对象中

从上面我们可以看到 Point 的 x  是 int  但是我们用 Beanutils 设置值的时候是用 Object  类型(通常是 String 类型)设置进去的,用 Beanutils 去取值的时候返回是 String 类型的。这是因为 web 前端通常使用的 String 类型的数据,和用户交互输入的也是 String 类型的数据,所以 Beanutils 直接支持 String 类型数据,免去了 String 到 基本类型数据的解析,和基本类型数据到 String 的转换。

package cn.itcast.reflect;  
  
import org.apache.commons.beanutils.BeanUtils;  
public class IntrospectorDemo {  

    public static void main(String[] args) throws Exception {  
  
        Point point = new Point(2, 5);  
        String propertieName = "x";  
        BeanUtils.setProperty(point, propertieName, "8");  
        System.out.println(point.getX());  
        System.out.println(BeanUtils.getProperty(point, propertieName));  
        System.out.println(BeanUtils.getProperty(point, propertieName).getClass()  
                .getName());  
  
        BeanUtils.setProperty(point, propertieName, 8);  
        System.out.println(BeanUtils.getProperty(point, propertieName).getClass()  
                .getName());  
        // 我们看到虽然属性x的类型是Integer,但是我们设置的时候无论是Integer还是String,BeanUtils的内部  
        // 都是当成String来处理的。  
    }  
      
    public static class Point {  
        private int x;  
        private int y;  
  
        public Point() {  
  
        }  
  
        public Point(Integer x, Integer y) {  
            super();  
            this.x = x;  
            this.y = y;  
        }  
  
        public Integer getX() {  
            return x;  
        }  
  
        public void setX(Integer x) {  
            this.x = x;  
        }  
  
        public Integer getY() {  
            return y;  
        }  
  
        public void setY(Integer y) {  
            this.y = y;  
        }  
    }  
}  
BeanUtils 操作的类必须是 public 修饰过的,所以这里吧 Point 设置 pulic  static 了,把 JavaBean 放在某个类的里面,这种在正式使用的时候,估计不会有这种设计,因为 JavaBean 本来就是公开的,用来传递数据的类。
BeanUtils支持javabean属性的级联操作
/** 
 * 需求:演示 BeanUtils 支持 javabean 属性的级联操作 
 */  
package cn.itcast.reflect;  
  
import java.util.Date;  
  
import org.apache.commons.beanutils.BeanUtils;  

public class BeanUtilsDemo {  

    public static void main(String[] args) throws Exception {  
        // TODO Auto-generated method stub  
        Person person = new Person();  
        String propertyName = "birthday.time";  
        System.out.println(BeanUtils.getProperty(person, propertyName));  
    }  
  
    public static class Person {  
        private Date birthday;  
  
        public Person() {  
            birthday = new Date();  
        }  
  
        public Date getBirthday() {  
            return birthday;  
        }  
  
        public void setBirthday(Date birthday) {  
            this.birthday = birthday;  
        }  
    }  
}  



Java7的新特性
Map map = (name:"xxx",age:"18");   // Map可以这样定义, 这和 json 有点像


PropertiesUtils类
java.lang.Object
      org.apache.commons.beanutils.PropertyUtils


Utility methods for using Java Reflection APIs to facilitate generic property getter and setter operations on Java objects.
static Object getProperty(Object bean, String name) 
static void setProperty(Object bean, String name, Object value) 


BeanUtils是以字符串的形式对javabean进行操作,
而PropertiesUtils是以属性本身的类型进行操作。
这是两者的区别。
实例:
//eanUtils工具包 和logging包实现对JavaBean的操作  
//利用Beanutils工具包可以对嵌套的JavaBean类中的属性进行操作   
BeanUtils.setProperty(jbc, "brithday.time", 111);  
System.out.println(BeanUtils.getProperty(jbc, "brithday.time"));    
//PropertyUtils工具类传入的参数和拿出的参数必须和属性本身的类型一致  
PropertyUtils.setProperty(jbc, "brithday.time", 123);  
System.out.println(PropertyUtils.getProperty(jbc, "brithday.time"));  
//利用BeanUtils工具实现JavaBean与Map的转换  
Map map = BeanUtils.describe(jbc);  

System.out.println(map);  

BeanUtils.setProperty(map, "name", "xx");  
System.out.println(map);  


Web 开发框架 Struts 中的 FormBean 就是通过内省机制来将表单中的数据映射到类的属性上,因此要求 FormBean 的每个属性要有 getter/setter 方法。但也并不总是这样,什么意思呢?就是说对一个 Bean 类来讲,我可以没有属性,但是只要有 getter/setter 方法中的其中一个,那么 Java 的内省机制就会认为存在一个属性,比如类中有方法 setMobile ,那么就认为存在一个 mobile 的属性。
将 Java 的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的 Struts ,还有用于处理 XML 文件的 Digester 项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。

注:自省和JavaBean不是很明白,总结学习中参考不少网络资料,在此向网络前辈们致敬。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值