在计算机科学中,内省是指计算机程序在运行时(Run time)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。 不应该将内省和反射混淆。相对于内省,反射更进一步,是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
内省和反射有什么区别?
反射是在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。 内省(IntroSpector)是Java 语言对 Bean 类属性、事件的一种缺省处理方法。 JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和事件,以后我们就可以操纵该JavaBean的属性。
在Java内省中,用到的基本上就是上述几个类。 通过BeanInfo这个类就可以获取到类中的方法和属性。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。 Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中,一般的做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法,这就是内省机制。
JDK内省类库
java.beans.Introspector:Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。 java.beans.BeanInfo接口:希望提供有关其 bean 的显式信息的 bean 实现者可以提供某个 BeanInfo 类,该类实现此 BeanInfo 接口并提供有关其 bean 的方法、属性、事件等显式信息。 java.beans.PropertyDescriptor:PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。内省代码实现
内省机制单元测试代码 package com.study.java.junit; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Date; import org.junit.Before; import org.junit.Test; import com.study.java.domain.User; /** * @Name: IntrospectorTest * @Description: JavaBean-API:内省机制测试类 * @Author: XXX * @CreateDate: XXX * @Version: V1.0 */ public class IntrospectorTest { private User user ; @Before public void init() { user = new User() ; user.setName("张三") ; user.setAge(21) ; user.setGender(true) ; user.setBirthday(new Date()) ; user.setAddress("北京丰台") ; } /** * @Name: getBeanPropertyInfo * @Description: 获取User-Bean的所有属性信息 * @Author: XXX * @Version: V1.0 * @CreateDate: XXX * @Parameters: @throws Exception * @Return: void */ @Test public void getBeanPropertyInfo() throws Exception { //获取User-BeanInfo对象:beanInfo是对一个Bean的描述,可以通过它取得Bean内部的信息 /** * 获取User-BeanInfo对象 * 1、Introspector类 * 是一个工具类,提供了一系列取得BeanInfo的方法; * 2、BeanInfo接口 * 对一个JavaBean的描述,可以通过它取得Bean内部的信息; * 3、PropertyDescriptor属性描述器类 * 对一个Bean属性的描述,它提供了一系列对Bean属性进行操作的方法 */ BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class) ; PropertyDescriptor[] pds = userBeanInfo.getPropertyDescriptors() ; for (PropertyDescriptor pd : pds) { Method method = pd.getReadMethod() ; String methodName = method.getName() ; Object result = method.invoke(user) ; System.out.println(methodName + "-->" + result); } } /** * @Name: getBeanPropertyByName * @Description: 获取指定属性名称的属性描述器,并对属性进行操作 * @Author: XXX * @Version: V1.0 * @CreateDate: XXX * @Parameters: * @Return: void */ @Test public void getBeanPropertyByName() throws Exception { //获取name属性的属性描述器 PropertyDescriptor pd = new PropertyDescriptor("name", user.getClass()) ; //得到name属性的getter方法 Method readMethod = pd.getReadMethod() ; //执行getter方法,获取返回值,即name属性的值 String result = (String) readMethod.invoke(user) ; System.out.println("user.name" + "-->" + result); //得到name属性的setter方法 Method writeMethod = pd.getWriteMethod() ; //执行setter方法,修改name属性的值 writeMethod.invoke(user, "李四") ; System.out.println("user.name" + "-->" + user.getName()); } } MyBeanUtils封装工具类 package com.study.java.utils; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Map; /** * @Name: MyBeanUtils * @Description: JavaBean属性操作工具类 * @Author: XXX * @CreateDate: 2017-3-9 上午10:40:01 * @Version: V1.0 */ public class MyBeanUtils { /** * @Name: getPropertyValue * @Description: 根据指定的属性名称获取属性值 * @Author: XXX * @Version: V1.0 * @CreateDate: 2017-3-9 上午10:55:17 * @Parameters: @param propertyName 属性名称 * @Parameters: @param bean bean实例对象 * @Return: Object 返回getter方法的返回值,即属性值 */ public static Object getPropertyValue(String propertyName, Object bean) { Object propertyValue = null ; try { PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass()) ; Method method = pd.getReadMethod() ; propertyValue = method.invoke(bean) ; } catch (Exception e) { e.printStackTrace(); } return propertyValue ; } /** * @Name: setProperty * @Description: 设置/修改属性的内容 * @Author: XXX * @Version: V1.0 * @CreateDate: 2017-3-9 上午11:01:23 * @Parameters: @param bean Bean实例对象 * @Parameters: @param name 属性名 * @Parameters: @param value 修改内容 * @Return: void 无 */ public static void setProperty(Object bean, String name, Object value) { try { PropertyDescriptor pd = new PropertyDescriptor(name, bean.getClass()) ; Method method = pd.getWriteMethod() ; method.invoke(bean, value) ; } catch (Exception e) { e.printStackTrace() ; } } /** * @Name: populate * @Description: 将Map中的内容封装到JavaBean * 说明: * Map中的key必须与JavaBean中的属性名称相同 * Map中的value传递给JavaBean对应的属性 * @Author: XXX * @Version: V1.0 * @CreateDate: XXX * @Parameters: @param bean * @Parameters: @param map * @Return: void */ public static Object populate(Object bean, Map<String, Object> map) { if(map != null && map.size() > 0) { for(Map.Entry<String, Object> entry : map.entrySet()) { String propertyName = entry.getKey() ; Object propertyValue = entry.getValue() ; try { PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass()) ; Method method = pd.getWriteMethod() ; method.invoke(bean, propertyValue) ; } catch (Exception e) { throw new RuntimeException(e) ; } } } return bean ; } } 工具类单元测试代码 package com.study.java.junit; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.junit.Before; import org.junit.Test; import com.study.java.domain.User; import com.study.java.utils.MyBeanUtils; /** * @Name: MyBeanUtilsTest * @Description: JavaBean属性操作工具测试类 * @Author: XXX * @CreateDate: XXX * @Version: V1.0 */ public class MyBeanUtilsTest { private User user ; @Before public void init() { user = new User() ; user.setName("张三") ; user.setAge(21) ; user.setGender(true) ; user.setBirthday(new Date()) ; user.setAddress("北京丰台") ; } @Test public void testSetPropertyValue() { System.out.println(user); //设置String类型数据 MyBeanUtils.setProperty(user, "name", "李思思") ; //设置int类型数据 MyBeanUtils.setProperty(user, "age", 23) ; //设置boolean类型数据 MyBeanUtils.setProperty(user, "gender", false) ; //设置Date类型数据 MyBeanUtils.setProperty(user, "birthday", new Date(13213412412L)) ; System.out.println(user); } @Test public void testGetPropertyValue() { //获取String类型属性 String name = (String) MyBeanUtils.getPropertyValue("name", user) ; //获取int类型属性 int age = (int) MyBeanUtils.getPropertyValue("age", user) ; //获取boolean类型属性 boolean gender = (boolean) MyBeanUtils.getPropertyValue("gender", user) ; //获取Date类型属性 Date birthday = (Date) MyBeanUtils.getPropertyValue("birthday", user) ; System.out.println(name + "," + age + "," + gender + "," + birthday + "."); } @Test public void testPopulate() { //向Map集合中封装数据,适用于request.getParameterMap() ; Map<String, Object> map = new HashMap<String, Object>() ; map.put("name", "王五") ; map.put("age", 21) ; map.put("gender", true) ; map.put("birthday", new Date(32131412L)) ; String[] hobbies = {"打球", "唱歌"} ; map.put("hobbies", hobbies) ; //将Map集合中的数据封装到UserBean User u = (User) MyBeanUtils.populate(new User(), map) ; System.out.println(u); } }总结: 由上述可看出,内省操作非常的繁琐,所以所以Apache开发了一套简单、易用的API来操作Bean的属性——BeanUtils工具包。对于该工具包API的作用及使用方法,我将在下一篇文章进行总结,这里先提供下BeanUtils工具包的下载地址:http://commons.apache.org/beanutils/;注意:BeanUtils的包依赖于logging包,logging包的下载地址为:http://commons.apache.org/logging/。