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方法,主要是要使字符串转换成对应类型的对象

原文地址:https://www.cnblogs.com/ixenos/p/5836059.html