1、首先了解JavaBean
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类的方法主要用于访问私有的字段。
例:写一个JavaBean
package com.itheima.introspector; public class Person { private String name;//定义一个name字段 private String sex;//定义一个sex字段 private int age;//定义一个age字段 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } |
在实际开发中,用户把大量的数据提交过来,我们需要用面向对象的思想去接收,把这些数据封装到一个对象中,这样的类就可以称作JavaBean。
2、什么是Java对象的属性和属性的读写方法
上面的Person对象中有三个字段,但此时不能称他们为属性,这些字段只有对外提供了getter和setter方法才能称作属性,就是说一个Bean的属性不是有上面的字段决定的,是有他们的getter和setter方法决定的。
如果上面的Person类添加代码如下
public String getAddress(){ return null; } |
请问现在Person类中一共有多少属性
通过思考,我们知道一共有五个属性,分别为:name,sex,age,address,class(继承Object)
3、内省(Introspector)
3.1、我们为什么学内省
我们知道在开发框架时,经常需要使用java对象的属性来封装程序中的数据,每次都使用反射技术完成此类操作比较麻烦,所以sun公司开发了一套API,专门用来操作java对象的属性。
3.2、内省访问JavaBean属性的两种方法
(1)通过PropertyDescriptor类操作Bean的属性 (2)通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法 |
方法一:通过第一种方法来操作Person的属性
package com.itheima.introspector; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.junit.Test; public class IntrospectorDemo { @Test public void test()throws Exception { // 创建一个Person对象 Person p = new Person(); Object value="黑马_1"; String propertyName="name"; //调用写方法,往对象中封转值 setProperty(p, propertyName, value); //从对象中取出属性的值 // 得到p对象的属性描述器 Object retVal = getProperty(p, propertyName); System.out.println(retVal); } //获取属性的读 private Object getProperty(Person obj, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { // 得到p对象的属性描述器 PropertyDescriptor pd=new PropertyDescriptor(propertyName, obj.getClass()); //得到属性的写方法 Method m=pd.getReadMethod(); //执行方法 Object retVal=m.invoke(obj); return retVal; } //获取属性的写方法 private void setProperty(Object p, String propertyName, Object value) throws IntrospectionException, IllegalAccessException, InvocationTargetException { // 得到p对象的属性描述器 PropertyDescriptor pd=new PropertyDescriptor(propertyName, p.getClass()); //得到属性的写方法 Method m=pd.getWriteMethod(); //调用invoke方法执行 m.invoke(p,value); } } |
运行结果:黑马_1
从上面的程序中我们可以看出利用属性描述器PropertyDescriptor,很容易对属性进行读和写的操作
方法二:
package com.itheima.introspector; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.junit.Test; public class IntrospectorDemo1 { @Test public void test()throws Exception { // 创建一个Person对象 Person p = new Person(); Object value="黑马_1"; String propertyName="name"; //调用写方法,往对象中写数据 setPerproty(p, propertyName, value); Object retVal = getProperty(p, propertyName); System.out.println(retVal); } //获取属性的读 private Object getProperty(Object obj, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { //首先通过Introspector中的getBeanInfo方法创建一个BeanInfo对象 BeanInfo info=Introspector.getBeanInfo(obj.getClass()); //然后调用BeanInfo中的方法得到所有的属性,然后 PropertyDescriptor[]pds=info.getPropertyDescriptors(); //利用增强for循环遍历 Object retVal=null; for(PropertyDescriptor pd:pds){ //如果正在操作的属性等于了指定的那个属性 if(pd.getName().equals(propertyName)){ //调用写方法 Method m=pd.getReadMethod(); retVal=m.invoke(obj); break; } } return retVal; } //获取属性的写方法 private void setPerproty(Object obj, String propertyName, Object value) throws IntrospectionException, IllegalAccessException, InvocationTargetException { //首先通过Introspector中的getBeanInfo方法创建一个BeanInfo对象 BeanInfo info=Introspector.getBeanInfo(obj.getClass()); //然后调用BeanInfo中的方法得到所有的属性,然后 PropertyDescriptor[]pds=info.getPropertyDescriptors(); //利用增强for循环遍历 for(PropertyDescriptor pd:pds){ //如果正在操作的属性等于了指定的那个属性 if(pd.getName().equals(propertyName)){ //调用写方法 Method m=pd.getWriteMethod(); m.invoke(obj, value); break; } } } } |
运行结果:黑马_1
从两个方法对比可以看出,第一种方法操作很方便,用的地方也很多,但是它只能操作一个属性,第二种方法操作比较复杂,它没有提供操作一个属性的方法,只提供了操作所有属性的方法。
4、第三方的工具:BeanUtils
我们知道,在实际应用中操作javabean属性的几率非常的高,利用上述的两种方法都非常的麻烦,所有有人写出了一个工具包,可以很方便的操作JavaBean的属性了。
在用BeanUtils之前首先需要将相关的开发包导入到我们的开发环境中去,主要有两个包如下:
(1)commons-beanutils-1.8.0.jar (2)Commons-logging.jar |
4.1、BeanUtils工具包中常用的类
(1)BeanUtils (2)Properties (3)ConvertUtils.regsiter(Converter convert, Class clazz) (4)自定义转换器 |
(1)BeanUtils类的主要方法
(1)public static void setProperty(Object bean, String name,Object value) 向bean中的指定属性添加值 (2)public static String getProperty(Object bean,String name) 获取某个指定属性的值 |
例:
package com.itheima.introspector; import org.apache.commons.beanutils.BeanUtils; import org.junit.Test; public class BeanUtilsDemo1 { @Test public void test1()throws Exception{ //创建一个Person对象 Person p=new Person(); //设置Person的name属性 BeanUtils.setProperty(p, "name","黑马_1"); //获取Person的name属性的值 String name=BeanUtils.getProperty(p, "name"); System.out.println(name); } } |
运行结果:黑马_1
例二:
package com.itheima.introspector; import org.apache.commons.beanutils.BeanUtils; import org.junit.Test; public class BeanUtilsDemo1 { @Test public void test1()throws Exception{ String name="黑马_1"; String sex="男"; String age="23"; //创建一个Person对象 Person p=new Person(); BeanUtils.setProperty(p, "name",name);//向p中设置name属性 BeanUtils.setProperty(p, "sex",sex);//向p中设置sex属性 BeanUtils.setProperty(p, "age",age);//向p中设置age属性 System.out.println(BeanUtils.getProperty(p, "name")); System.out.println(BeanUtils.getProperty(p, "sex")); System.out.println(BeanUtils.getProperty(p, "age")); } } |
运行结果:黑马_1
男
23
但是对上面的程序有一个疑问,在Person中我们定义的age的类型为int,而我们测试的时候明明String age=”23”;,为什么能够运行成功的。
这个问题就涉及到了BeanUtils中的类型转换器了,Beanutils中内置了8中基本数据类型的类型转换器,可以自动的进行数据类型的转换,但是如果是别的类型,就会出现错误,如下代码:
在Person中加如下代码
private Date birthday;//定义一个Date类型的属性 public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } |
如果这样设置属性
String birthday=”1999-11-10”; BeanUtils.setProperty(p, "birthday",birthday) |
就会出现类型转换异常的错误,这是因为Date没有在Beanutils中设置它的类型的转换,要想运行成功,需要注册一个自定义的转换器
ConvertUtils类的register方法
(5)ConvertUtils.regsiter(Converter convert, Class clazz) |
上面的代码加如下代码:
//为了让日期赋值到bean中的birthday属性上,我们被beanutils注册一个日期转换器 ConvertUtils.register(new Converter() { @Override public Object convert(Class type, Object value) { //判断value是否为null if(value==null){ return null; } //首先判断value是否为String类型的 if(!(value instanceof String))//如果不是String类型抛出异常 throw new ConversionException("只支持String类型的转换"); String str=(String)value; if(str.trim().equals("")){ return null; } //创建SimpleDateFormart对象 SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); try { return sdf.parse(str);//转换类型 } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); |
通过上面的注册类型转换器,就可以正常的运行了
黑马_1
男
23
Wed Nov 10 00:00:00 GMT 1999
这样的类型转换器用处非常的广泛,以后我们学习框架的时候例如Struts1,Struts2时,就需要利用类型转换器了。
BeanUtils中Map集合的应用。
public static void populate(Object bean, Map properties) 把Map集合中数据填充到bean中 |
例:
@Test public void test2()throws Exception{ Map map=new HashMap();//创建一个Map集合 map.put("name", "黑马_2");//向集合中添加元素 map.put("sex", "男");//向集合中添加元素 map.put("age", "23");//向集合中添加元素 map.put("birthday", "1999-11-10");//向集合中添加元素 Person p=new Person(); //为了让日期赋值到bean中的birthday属性上,我们被beanutils注册一个日期转换器 ConvertUtils.register(new Converter() { @Override public Object convert(Class type, Object value) { //判断value是否为null if(value==null){ return null; } //首先判断value是否为String类型的 if(!(value instanceof String))//如果不是String类型抛出异常 throw new ConversionException("只支持String类型的转换"); String str=(String)value; if(str.trim().equals("")){ return null; } //创建SimpleDateFormart对象 SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); try { return sdf.parse(str);//转换类型 } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); //把Map集合中数据填充到对象p中 BeanUtils.populate(p, map); System.out.println(p.getName()); System.out.println(p.getSex()); System.out.println(p.getAge()); System.out.println(p.getBirthday()); } |
这样我们就把map集合中的数据填充到了person对象中,这在javaWeb中会经常的用到。例如我们通过request传递的数据通常会放到map集合中,所以我们能够通过这个方法把map集合中的数据填充到bean中。