• JavaEE JavaBean 反射、内省、BeanUtils


    JavaEE JavaBean 反射、内省、BeanUtils

    @author ixenos

    JavaBean是什么


    一种规范,表达实体和信息的规范,便于封装重用。

    1、所有属性为private
    2、提供默认构造方法
    3、提供getter和setter
    4、实现serializable接口

     1 public class Person implements Serializable{
     2     private int age;
     3     private String name;
     4 
     5     public Person(){}
     6 
     7     public Person(int age, String name){
     8         this.age = age;
     9         this.name = name;
    10     }
    11 
    12     public void setAge(int age){
    13         this.age = age;
    14     }
    15     public void setName(String name){
    16         this.name = name;
    17     }
    18     public int getAge(){
    19         return this.age;
    20     }
    21     public String getName(){
    22         return this.name;
    23     }
    24 }

    现有一需求:封装JavaBean数据


      由于不知JavaBean具体类型,所以编写一个工厂方法,根据配置文件内容中的一些属性数据,把对象的属性数据封装到对象(JavaBean)中,工厂方法返回对应的对象(JavaBean)。 

      在没学任何工具之前,作为工厂方法,第一时间想到的自然是利用反射创建对象。

    反射的思路:解析配置文件,获取Field

      1.通过流读取配置文件,获取到完整的类名

      2.由类名获得Class对象,此时先newInstance获得一个默认JavaBean

      3.通过流读取配置文件,由正则表达式分别获取变量名和变量值

      4.根据变量名由Class对象获得Field对象,然后调用set方法设置默认JavaBean的属性数据

     1 import java.io.BufferedReader;
     2 import java.io.FileReader;
     3 import java.lang.reflect.Constructor;
     4 import java.lang.reflect.Field;
     5 
     6 /**  
     7  *  反射的思路,解析配置文件,获取Field
     8  *
     9  *  @author ixenos
    10  *
    11  */
    12 public class Demo1 {
    13     
    14     public static void main(String[] args) throws Exception {
    15         Person p = (Person)getInstance();
    16         System.out.println(p);
    17     }
    18 
    19     //根据配置文件的内容产生对象的对象,并且要把对象的属性值封装到对象中
    20     public static Object getInstance() throws Exception{
    21         BufferedReader bufferedReader = new BufferedReader(new FileReader("obj.txt"));
    22         String className = bufferedReader.readLine();//读取配置文件,获取到完整的类名
    23         Class<?> clazz = Class.forName(className);
    24         //通过class对象获取到无参构造方法
    25         Constructor<?> constructor = clazz.getConstructor();
    26         //通过构造器对象创建对象
    27         Object o = constructor.newInstance();
    28         //读取属性值
    29         String line = null;
    30         while((line = bufferedReader.readLine()) != null){
    31             /* 
    32                split字符串,根据给定正则表达式的匹配拆分此字符串,返回String[]数组
    33                      左边的为datas[0],右边的为datas[1]
    34             
    35              */
    36             String[] datas = line.split("=");
    37             //通过属性名获取到对应的Field对象
    38             Field field = clazz.getDeclaredField(datas[0]);
    39             if(field.getType() == int.class){
    40                 field.set(o, Integer.parseInt(datas[1]));
    41             }else{
    42                 field.set(o, datas[1]);
    43             }
    44         }
    45         bufferedReader.close();
    46         
    47         return null;
    48     }
    49 }

      开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API:内省(Intorspector),专门用于操作java对象的属性。

    内省(Introspector)


    内省原理:

      1.读取配置文件信息

      2.根据信息利用反射构建Class对象、默认JavaBean和具体的set和get方法的Method对象

      3.如果一个类中没有setter和getter方法,那么内省就没用了,因为内省是根据这两个方法来操纵属性数据的

      因此内省是一个变态的反射,与上面反射思路不同在于默认读取JavaBean,由Method对象来set

    为什么要学内省?

    内省是用于操作java对象的属性的,那么以下问题我们必须要清楚。

    问题一: 什么是Java对象的属性和属性的读写方法?

      答: 非静态Field及其setter和getter

    问题二: 如何通过内省访问到javaBean的属性 ?

      答:内省有两种方式

        1.通过PropertyDescriptor类操作JavaBean的某个属性,获得已知对象某个属性的setter和getter方法

     1     /*
     2         通过属性描述器,获得已知对象某个属性的setter和getter方法,从而来填入属性
     3     */
     4     public void testProperty() throws Exception {
     5         Person p = new Person();
     6         //属性描述器 (property即是属性)
     7         PropertyDescriptor descriptor = new PropertyDescriptor("id", Person.class);
     8         //获取属性对应的get或者set方法来设置或者获取属性
     9         Method m = descriptor.getWriteMethod();//获取属性的set方法
    10         //执行该方法设置属性值
    11         m.invoke(p, 100);
    12         Method readMethod = descriptor.getReadMethod();//获取属性的get方法
    13         System.out.println(readMethod.invoke(p));
    14         
    15     }

        

         2.通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取所有PropertyDescriptor,

          通过这个属性描述器就可以获取每个属性对应的 getter/setter 方法,

     1     /*
     2         通过BeanInfo获得一个类中的所有属性描述器
     3     */
     4     public void getAllProperty() throws IntrospectionException{
     5         //IntroSpector 内省类
     6         BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
     7         //通过BeanInfo获取所有的属性描述器
     8         PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();//获取一个类中的所有属性描述器
     9         for(PropertyDescriptor p : descriptors){
    10             System.out.println(p.getReadMethod());//获取一个类中所有的get方法
    11         }
    12         
    13     }

      内省依旧存在的问题: 

    sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单、易用的API操作Bean的属性——BeanUtils。

    Apache的BeanUtils


    Apache的BeanUtils和Sun的IntroSpector主要解决的问题都是: 把对象的属性数据封装到对象中

      而且同样依赖JavaBean的setter和getter方法(Method, not Field)

    BeanUtils的好处

    1. BeanUtils设置属性值的时候,如果属性是基本数据类型BeanUtils会自动转换数据类型

    2. BeanUtils设置属性值的时候,如果属性是引用数据类型,那么这时候必须要注册一个类型转换器

    3. BeanUtils设置属性值的时候底层也是依赖setter和getter方法设置以及获取属性值的。

      设置基本数据类型示例:

     1 import java.text.SimpleDateFormat;
     2 import java.util.Date;
     3 
     4 import org.apache.commons.beanutils.BeanUtils;
     5 
     6 public class Demo3 {
     7 
     8     public static void main(String[] args) throws Exception {
     9         //从文件中读取到的数据都是字符串的数据,或者是表单提交的数据获取到的时候也是字符串的数据。
    10         String id ="110";
    11         String name="ixenos";
    12         String salary = "1000.0";
    13         
    14         Emp e = new Emp();
    15                 //对应JavaBean,属性名(字符串),属性(变量)    
    16         BeanUtils.setProperty(e, "id", id); 
    17         BeanUtils.setProperty(e, "name",name);
    18         BeanUtils.setProperty(e, "salary",salary);
    19             
    20         System.out.println(e);        
    21     }
    22 }
    23     

       设置引用类型示例:

     1 import java.text.SimpleDateFormat;
     2 import java.util.Date;
     3 
     4 import org.apache.commons.beanutils.BeanUtils;
     5 import org.apache.commons.beanutils.ConvertUtils;
     6 import org.apache.commons.beanutils.Converter;
     7 
     8 /*
     9  
    10        BeanUtils设置属性值,如果设置的属性是其他的引用 类型数据,那么这时候必须要注册一个类型转换器。
    11 
    12  */
    13 public class Demo3 {
    14 
    15     public static void main(String[] args) throws Exception {
    16         //从文件中读取到的数据都是字符串的数据,或者是表单提交的数据获取到的时候也是字符串的数据。
    17         String id ="110";
    18         String name="ixenos";
    19         String salary = "1000.0";
    20         String birthday = "2013-12-10";//引用类型使用BeanUtils要注册类型转换器
    21         
    22         //注册一个类型转换器
    23         ConvertUtils.register(new Converter() {
    24 
    25             @Override
    26             public Object convert(Class type, Object value) { // type : type to which this value should be converted。 将在register填入Date.class
    27                 Date date = null;
    28                 try{
    29                     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    30                     date = dateFormat.parse((String)value);
    31                 }catch(Exception e){
    32                     e.printStackTrace();
    33                 }
    34                 return date;
    35             }
    36             
    37         }, Date.class);
    38         
    39         Emp e = new Emp();
    40         BeanUtils.setProperty(e, "id", id);
    41         BeanUtils.setProperty(e, "name",name);
    42         BeanUtils.setProperty(e, "salary",salary);
    43         BeanUtils.setProperty(e, "birthday",birthday);
    44         
    45         System.out.println(e);    
    46     }
    47 }

    相关方法签名

    public static void setProperty(Object bean, String name, Object value)

      形参对应JavaBean,属性名,属性(基本数据类型已注册,引用类型要手动注册才可调用此方法)

    public static void register(Converter converter, Class clazz)

      形参对应Converter,属性类型对象

      Converter是个接口,有一些实现类可用,也可自行(用匿名对象)实现

      public Object convert(Class type, Object value)

      // type : type to which this value should be converted,将在register填入Date.class

      即clazz将自动填入type

      实现Converter接口需要重写其中的convert方法,主要是要使字符串转换成对应类型的对象

  • 相关阅读:
    超大文件排序
    透彻理解迪杰斯特拉算法
    Floyd-傻子也能看懂的弗洛伊德算法(转)
    轻松实现在浏览器上播放本地视频
    Caffeine缓存处理
    每日日报94
    每日日报93
    下载安装SQL server2008的步骤
    每日日报92
    每日日报91
  • 原文地址:https://www.cnblogs.com/ixenos/p/5836059.html
Copyright © 2020-2023  润新知