黑马程序员技术交流社区

标题: JavaBean内省的问题 [打印本页]

作者: 邱成    时间: 2012-7-12 07:15
标题: JavaBean内省的问题
什么是内省,JavaBean中直接调用get与set方法不就行了,为什么要弄一个内省?
作者: 孙新强    时间: 2012-7-12 09:20
开发框架时,经常需要使用 java 对象的属性来封装程序的数据,每次都使用反射技术 完成此类操作过于麻烦,所以要用内省!
那又要说为什么要运用反射来开发框架了...........
因为框架都是提前写好的,而你自己写的类都是在框架写完之后,所以要用反射..(这只是我个人简单的理解)

开发框架时,经常需要使用 java 对象的属性来封装程序的数据,每次都使用反射技术 完成此类操作过于麻烦,所以要用内省!
那又要说为什么要运用反射来开发框架了...........
因为框架都是提前写好的,而你自己写的类都是在框架写完之后,所以要用反射..(这只是我个人简单的理解)

作者: yjf    时间: 2012-7-12 09:33
框架的需要啊
作者: 耿立刚    时间: 2012-7-12 10:02
内省(IntroSpector)是Java 语言对 Bean 类属性、事件的一种缺省处理方法。例如类 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前缀,然后取剩余部分,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。下面就用前面的讲解的反射方式来设置或读取JavaBean的属性:
         import java.beans.BeanInfo;
         import java.beans.IntrospectionException;
         import java.beans.Introspector;
         import java.beans.PropertyDescriptor;
     除了反射用到的类需要引入外,内省需要引入的类如上所示,它们都属于java.beans包中的类,自己写程序的时候也不能忘了引入相应的包或者类。下面代码片断是一个设置某个JavaBean类某个属性的关键代码:
         private static void setObjectProperty(ReflectPoint pt1, int value,String propName) throws IntrospectionException, IllegalAccessException, InvocationTargetException {
             /*BeanInfo bean = Introspector.getBeanInfo(ReflectPoint.class);
             PropertyDescriptor [] properties = bean.getPropertyDescriptors();
             for(PropertyDescriptor property: properties)
             {
                 if(property.getName().equals(propName))
                 {
                     Method mSetX = property.getWriteMethod();
                     mSetX.invoke(pt1, value);
                     break;
                 }
             }*/
             PropertyDescriptor property = new PropertyDescriptor("x",ReflectPoint.class);
             Method mSetX = property.getWriteMethod();
             mSetX.invoke(pt1, value);
         }
     代码中注释的语句代表一种方式,最后的三行代表另外一种设置相应类相应属性的方式。从简洁上来说,第二种方式显然更优雅。第一种方式中首先是获得相应javaBean的类信息bean,用的BeanInfo这个类来描述的。然后就能够通过getPropertyDescriptors方法获取属性描述数组对象。最后从数组中取去一个个的属性对象进行操作——获取相应的读写方法,然后调用相应的读写方法。这些是通过反射机制做到的。由于这种方式比较麻烦,如果只调用其中一种方法,就要遍历一次属性描述数组,所以就产生了第二种方式,直接通过传入属性名称,相应的javaBean类型参数,构造出一个需要的PropertyDescriptor对象,然后再进行调用。
     理解了相应的原理,那些现成的工具用起来就会更舒服,如Beanutils与PropertyUtils工具。这两个工具设置属性的时候一个主要区别是PropertyUtils.getPropety方法获得的属性值的类型为该属性本来的类型,而BeanUtils.getProperty则是将该属性的值转换成字符串后才返回。
作者: 苑占丽    时间: 2012-7-12 10:30
JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。
如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?
用内省这套api操作JavaBean比用普通类的方式更方便。

比如说有一个Person类
public class person{
    private String name;
    private int age;
    /*这里是对name和age操作的get和set方法  */
}
然后有一个对Person类操作的PersonDao,里面就有很多方法啊,比如增删改查,方法的参数可以是一个Person对象啊,这个时候如果你在方法中要得到哪个属性值就可以直接对象名.getX就行啊,这样操作不是比内省方便么?
但是!!!!举个列子吧:
在你做Servlet/JSP的web编程的时候,你的Servlet要经常接收客户端传过来的表单,现在
第一种表单传过来的为:
username : "zhangsan"
password : "lisi"
你有一个User的类,User类中有两个属性username和password

01.public class User{

02.        private String username;

03.        private String password;

04.        public String getUsername() {

05.                return username;

06.        }

07.        public void setUsername(String username) {

08.                this.username = username;

09.        }

10.        public String getPassword() {

11.                return password;

12.        }

13.        public void setPassword(String password) {

14.                this.password = password;

15.        }

16.        

17.}
复制代码现在你要讲传过来的值包装成一个User的对象,你怎么做?
普通方法是

01.String username = request.getParameter("username");

02.String password = request.getParameter("password");

03.User u = new User();

04.u.setUsername(username);

05.u.setPassword(password);
复制代码好像还简单是吧???
加入现在又第二种表单:
name : "wangwu"
age : 26
有一个Person类

01.public class Person{

02.        private String name;

03.        private int age;

04.        public String getName() {

05.                return name;

06.        }

07.        public void setName(String name) {

08.                this.name = name;

09.        }

10.        public int getAge() {

11.                return age;

12.        }

13.        public void setAge(int age) {

14.                this.age = age;

15.        }

16.        

17.}
复制代码要将上面的两个参数封装成一个Person对象,那么你就要重复
String name = request.getParameter("name");
String age = request.getParameter("age");
Person p = new Person();
p.setName(name);
p.setAge(Integer.parse(age));
这段程序和上面那段程序是不是很像,只是要创建的对象不一样而已,
而如果用放射的方式,就可以只写一个函数就可以了:
因为从request中是可以直接得到字段和值的map的,所以就可以利用反射

01.public class RequestUtil{

02.        public static Object getBean(Class c, Map map){

03.                Object o = c.newInstance();

04.                // 下面就是用反射的方式,从mao中得到key值就是o对象的属性的名称,value就是要设置的值

05.                // 是不是只要这个方法就可以对传过来的值就行封装

06.                /// ......具体实现可以参照视频的方式自己实现

07.                return o;

08.        }

09.}
复制代码写好上面的函数都,就可以把上面的两段话改写成


1、 User u = (User)RequestUtil.getBean(User.class,map);
2、 Person p = (User)RequestUtil.getBean(Person.class,map);

是不是比原来简单多了
当然你要先从request中获取map,这个获取就是调用一个函数的事情,很简单。

用张老师的说法就是,你确定你要操作的JavaBean对象了么?如果你没确定,新来一个又怎么样呢?改源码么?改源码又引发一系列问题怎么办?JavaBean的内省就是干的这事,不管你以后要来哪个类,只要你是JavaBean,配置文件上一方,就可以帮你改了~~~框架思想~~~~
作者: 王达    时间: 2012-7-12 10:48
因为反射一般用于框架的设计,框架都是在你定义类之前存在的,并不知道会出现什么样的类,直到自定义类完成了才能确定类的元素属性,一般用配置文件的形式进行设置,而框架利用流或类加载器的方法,读取配置文件中内容进行应用,而内省是基于反射的一种应用,它可以较简单的对其私有字段进行操作。
package cn.com.Test.BeanDemo;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Properties;
import org.apache.commons.beanutils.BeanUtils;
public class BeanTest {
public static void main(String[] args)
{
  BeanDemo beandemo=new BeanDemo();//可以用配置文件的方式得到对象,这里是为了简便直接创建了。
  try {
   String  propertyName="name";//可以从配置文件里得到
   String propertyName2="age";//可以从配置文件里得到   

   PropertyDescriptor descriptor=new PropertyDescriptor("name", BeanDemo.class);
   Method methodSet=descriptor.getWriteMethod();//得到set方法
   Object obj1=methodSet.invoke(beandemo, "lisi");//使用set方法进行设置
   
   Method methodGet=descriptor.getReadMethod();//得到get方法
   Object obj2=methodGet.invoke(beandemo);//使用get方法
   System.out.println(obj2);
   
   /*------以下用的是BeanUtil工具-------*/
   BeanUtils.setProperty(beandemo, propertyName, "zhaowu");//直接静态函数就能设置。
   Object obj3=BeanUtils.getProperty(beandemo, propertyName);//得到参数的值。
   System.out.println(obj3);
   
   BeanUtils.setProperty(beandemo, propertyName2, "14");//如果这里的14类型是int型也要转换成String型传入;
   Object obj4=BeanUtils.getProperty(beandemo, propertyName2);
   System.out.println(obj4);
   
   
  } catch (Exception e) {
   // TODO: handle exception
   System.out.println(e.toString());
  }
  
}
}
//这是自定义类:
package cn.com.Test.BeanDemo;
public class BeanDemo {
private String name="zhangsan";
private int age=22;
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;
}
}







欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2