Bean 内省 以及BeanUtils工具

JavaBean

为什么要学习内省

         开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术

完整此类操作过于麻烦,所以sun公司开发了一套API专门用于操作java对象的属性

什么是java对象的属性和属性的读写方法?

内省访问javaBean属性的两种方式?

1.      通过PropertyDescriptor类操作Bean的属性

2.      通过Intorspector类获得Bean对象的BeanInfo,然后用过BeanInfo来获取属性的描述器

(PropertyDescriptor),通过这个描述器就可以获取某个属性对应的getter、setter方法,

然后通过反射机制来调用这些方法

       JavaBean是一种方法符合某种特定规则的特殊java类。 

javaBean类里面的变量为私有,通过公共方法setX、getX;设置和拿到里面的方法

如下图:就是一个javaBean类

package cn.itcast.day1;
public class ReflectPoint {       
         private int x;
         private int y;    //这个只是字段,只有对外提供了get,set方法才能成为属性
     //javabean有对应的get、set方法就是bean方法  如果单独getAB()那么Bean就有AB属性  由于是从Object类继承 那么还有一个属性getClass类
         public int getAB() {
              return x;
         }
 
         public ReflectPoint(int x, int y){
              super();
              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;
         }
}


javaBean的内省操作

new一个ReflectPoint对象

 ReflectPoint pt1 = new ReflectPoint(3, 5);
 //定义一个名字属性
 String propertyName = "x";
 //在pt1上调用拿到属性的方法
 Object retVal = getProperty(pt1,propertyName);

定义一个getProperty(ReflectPointpt1, String propertyName);

带参的方法里面传两个参数,ReflectPointpt1和String propertyName

Introspector类为通过工具学习有关目标Java Bean支持的属性、事件和方法的知识提供了一个标准的方法。

是内省的入口类 这个类到底省谁?就要调用BeanInfo方法,然后把传入的bean类省出来,把所有的属性封装到BeanInfo方法中,拿到BeanInfo就相当于拿到了Bean的所有属性虽然拿到的是接口,这个接口负责返回接口的实例,相当于拿到对象了,就相当于叨叨所有的属性了,然后调用getPropertyDescriptors()方法,就可以拿到每一个属性的属性描述器,然后返回一个数组,数组里面装的是每一个属性的属性描述器,进而就可以调用属性描述其的getReadMethd获取应该属性的读取方法,相当于get方法,进而调用getWriteMethd就可以调用属性的写方法,得到属性的set方法就可以设置属性


使用内省API操作Bean属性

public static void test2() throws Exception {
       Person p = new Person();
       //创建一个属性构造器,第一个参数传要修改的属性名,第二个传Bean类的字节码
       PropertyDescriptor pd = new PropertyDescriptor("age",Person.class);
       //调用属性构造器的getWriteMethod()对其属性进行set操作。。。得到属性的写方法对其进行赋值
       Method md = pd.getWriteMethod();      //public void setAge()
       //调用invoke方法,第一个参数传Bean类,第二传要设置的值
       md.invoke(p, 45);
       //System.out.println("p.age:="+p.getAge());
       //通过调用获取read方法
       md = pd.getReadMethod();
       System.out.println(md.invoke(p, null));
 }
  

         //高级点的内容
         public static void test3() throws Exception {
               Person p = new Person();
               //创建一个属性构造器
               PropertyDescriptor pd = new PropertyDescriptor("age",Person.class);
               Class<?> cls = pd.getPropertyType();
               System.out.println(cls);
         }


对于这三种信息,Instrospector将分别分析bean的类和超类,寻找显示和隐式信息,使用这些信息构建一个全面描述目标bean和beanInfo对象。

对于每个“Foo”类,如果存在相应的“FooBeanInfo”类,显式信息可能是可用的,查询这些信息时,FooBeanInfo类会提供一个非Null值。通过获得目标bean类的完全受限定包名称并追加“BeanInfo”形成一个新类,首先查找BeanInfo类。如果此操作失败,则采用此名称的最终类名称组件,在BeanInfo包搜索路径中指定的每个包中搜索该类。

因此对于某个类,比如说“sun.xyz.OurButton”,首先需要查找称为

“sun.xyz.OurButtonInfo”的BeanInfo类,如果失败,则查找BeanInfo搜索路径,这意味着将查找“sun.beans.infos.OurButtonBeanInfo”.

如果没有再某个类提供有关其自身的显式BeanInfo,则将从分析其他派生类得到的信息添加到BeanInfo信息中,并将显式信息视为当前类和基类的权威信息,无需进一步深入超类链接进行分析。

如果没有再某个类上发现显式BeanInfo,则使用低层次的反射来研究类的方法,并标准设计模式来标识属性储存器,事件源或公共方法。然后深入分析的超类,从他那里(可能超类链的顶部添加信息)

 

PropertyDescriptor

public class PropertyDescriptor extends FeatureDescriptor

描述 Java Bean 通过一对存储器方法导出的一个属性。 

使用Introspector类中的静态方法getBeanInfo()方法      

getBeanInfo(Class<?>beanClass)   beanClass –是将要分析的 bean 类。 

FeatureDescriptor类是 PropertyDescriptor、EventSetDescriptor 和MethodDescriptor 等的公共基类。 

它支持一些可以设置和检索任意的内省描述符(introspection descriptor)的公共信息。

此外,它还提供了一种扩展机制,从而任意属性/值对都可以与设计特性相关联。

 

内省(IntroSpector)是Java语言对JavaBean类属性、事件的一种缺省处理方法。

例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。

通过getName/setName来访问name属性,这就是默认的规则。

Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。

 

一般的做法是通过类Introspector的getBeanInfo方法获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。

 

我们又通常把javabean的实例对象称之为值对象(Value Object),因为这些bean中通常只有一些信息字段和存储方法,没有功能性方法。

 

一个JavaBean类可以不当JavaBean用,而当成普通类用。JavaBean实际就是一种规范,当一个类满足这个规范,这个类就能被其它特定的类调用。一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。去掉set前缀,然后取剩余部分,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。

 

除了反射用到的类需要引入外,内省需要引入的类如下所示,它们都属于java.beans包中的类,自己写程序的时候也不能忘了引入相应的包或者类。下面代码片断是设置某个JavaBean类某个属性的关键代码:

private static Object getProperty(ReflectPoint pt1, String propertyName) throws Exception { 
         // Introspector为学习javaBean支持的属性、事件、方法提供了一套标准的方法
         // 在Java Bean上进行内省,了解其所有属性、公开的方法和事件。
         // getBeanInfo(Class<?> beanClass)里面传入的参数是  beanClass –是将要分析的 bean类
         BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());            
         //得到所有属性的描述
         // PropertyDescriptor类是用来描述Java Bean通过一对存储器方法导出的一个属性。
         // Property属性PropertyDescriptor属性描述器
         PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
         Object retVal = null;
         for(PropertyDescriptor pd : pds){
         //在数组中查找有没有与之相同的方法,如果有获取方法的对象,则调用此方法
         // pd.getName()是PropertyDescriptor从父类FeatureDescriptor继承来的方法
         //获得此特性的编程名称。 返回:属性/方法/事件的编程名称
         if(pd.getName().equals(propertyName)){
         // Method  getReadMethod() 获得应该用于读取属性值的方法。
         //应该用于读取属性值的方法。如果无法读取该属性,则可能返回。
         Method methodGetX = pd.getReadMethod();
         //调用此方法
         retVal = methodGetX.invoke(pt1);                             
      }
    }
  return retVal;
 }
}

通过set Property(pt1,propertyName,value);对其属性进行设置

    Object value = 7;
    setProperty(pt1,propertyName,value);
    System.out.println(pt1.getX());
set Property ReflectPoint pt1, StringpropertyName,Object value)

传入三个参数

1.      是要拿到的javaBean对象ReflectPoint pt1

2.      拿到属性name名字String  propertyName 3.传入设置的value值

通过PropertyDescriptor的构造方法PropertyDescriptor(StringpropertyName, Class<?> beanClass) 新建一个对象

再设置写入的方法

最后调用方法 

private static void setProperty(ReflectPoint pt1, String propertyName,
        Object value) throws IntrospectionException, IllegalArgumentException, IllegalAccessException,                              InvocationTargetException {
   //构造方法PropertyDescriptor(String propertyName, Class<?> beanClass)
   //属性名字,和bean类的字节码
   //通过调用 getFoo 和 setFoo 存储器方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor
   PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
   //获得应该用于写入属性值的方法。
   Method methodSetX = pd2.getWriteMethod();
   methodSetX.invoke(pt1, value);                  
}

使用BeanUtils工具,直接可以设置里面的属性

 public static void test1() throws Exception {
    Person p = new Person();
    //(Object bean,String name,Object value)
    //第一个是bean 第二个是bean属性的名字,第三个是值
    BeanUtils.setProperty(p, "age", 35);                    
    System.out.println(p.getAge());                    
}
在设置表单的情况下接受过来的String类型,但是可以通过BeanUtils工具直接进行转化

转化只支持八种基本数据类型

public static void test2() throws Exception {
      String name = "aaaa";
      String age = "34";            
      Person p = new Person();
      BeanUtils.setProperty(p, "name", name);
      //由于age是int类型,那么在BeanUtils后面做了很多的工作
      //自动把String转化成了整数赋值上去了
      BeanUtils.setProperty(p, "age", age);
                  
      System.out.println(p.getName());
      System.out.println(p.getAge());               
}

在里面添加转化器,由于BeanUtils只支持8种基本数据类型转化,所以对Date类型格式的转换要写进去一个构造器,Converter();由于是一个接口,写一个匿名内部类直接实例化里面的对象。然后对字符串进行三次检查,

第一是value值是否存在,

第二是通过instanceof 判断value是否是String的子类或者实例对象,如果是可以进行转换,不是则抛异常。

第三对字符串进行转换并去掉空格判断是否为空字符串

           public static void test3() throws Exception {
                   String name = "aaaa";
                   String age = "34";
                   String birthday = "1980-09-09";
                  
                   /**
                    * 为了让日期赋值到bean的birthday属性上。我们给BeanUtils
                    * 上注册一个转化器。ConvertUtils工具
                    * ConvertUtils.register(converter, clazz);
                    * 第二个属性告诉转化器是转什么类型的 这里是Date类型
                    * 第一个写一个转化器Converter converter是一个接口,
                    * new 并实例化其对象
                    *
                    */
                   ConvertUtils.register(new Converter(){
                            public Object convert(Class type, Object value) {
                                     //数据要先检查后使用
                                     if(value==null){
                                               return null;
                                     }
                                     /**
                                      * java中的instanceof运算符是用来在运行时指出对象是否
                                      * 是特定类的一个实例。instanceof通过返回一个布尔值来指出,
                                      * 这个对象是否是这个特定类或者是它的子类的一个实例。
                                      * result = object instanceof class
                                      *  Result:布尔类型。
                                      *  Object:必选项。任意对象表达式。
                                      *  Class:必选项。任意已定义的对象类。
                                      */
                                     if(!(value instanceof String)){
                                               throw new ConversionException("爷爷只支持String类型的转换");
                                     }
                                     String str = (String) value;
                                     if(str.trim().equals("")){
                                               return null;
                                     }
                                     //只有value闯过这三关就能转
                                     SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                                     try {
                                               return df.parse(str);
                                     } catch (ParseException e) {
                                               throw new RuntimeException();//异常链不能断
                                     }                                   
                            }
                           
                   }, Date.class);
                  
                  
                   Person p = new Person();
                   BeanUtils.setProperty(p, "name", name);
                   BeanUtils.setProperty(p, "age", age);
                   BeanUtils.setProperty(p, "birthday", birthday);
                  
                   System.out.println(p.getName());
                   System.out.println(p.getAge());
                   System.out.println(p.getBirthday());
                  
         }


        //也可以使用直接写好的转化器,但是这个转化器有bug,如果传入的格式不对就会出现异常
         public static void test4() throws Exception {
                   String name = "aaaa";
                   String age = "34";
                   String birthday = "1980-09-09";
        
                   ConvertUtils.register(new DateLocaleConverter(), Date.class);
        
                  
                   Person p = new Person();
                   BeanUtils.setProperty(p, "name", name);
                   BeanUtils.setProperty(p, "age", age);
                   BeanUtils.setProperty(p, "birthday", birthday);
                  
                   System.out.println(p.getName());
                   System.out.println(p.getAge());
                   System.out.println(p.getBirthday());
                  
         }
 
         public static void test5() throws Exception{
                   Map map = new HashMap();
                   map.put("name", "aaa");
                   map.put("age", "23");
                   map.put("birthday", "1980-09-09");
                  
                   //这里也要提前注册转化器
                   ConvertUtils.register(new DateLocaleConverter(), Date.class);
                   Person bean = new Person();
                   //用map集合的值,填充到bean属性
                   BeanUtils.populate(bean, map);
                  
                   //修改名字的时候ctrl+1 rname
                   System.out.println(bean.getName());
                   System.out.println(bean.getAge());
                   System.out.println(bean.getBirthday());                  
       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值