内省(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 方法,然后我们就可以通过反射机制来调用这些方法。
下面我们来看看JDK中Introspection中主要的类与其类关联图:
我们可以在这个类图中看到内省提供了我们动态修改JavaBean的信息。
1、PropertyDescriptor
下面我们根据国际惯例来一个内省的HelloWorld吧。
1) User
/**
* Created by Carl on 2016/11/26.
*/
@Data
public class User implements Serializable {
private static final long serialVersionUID = -7441530543119157958L;
private String username;
private int age;
}
2) IntrospectionTest
/**
* Created by Carl on 2016/11/26.
*/
public class IntrospectionTest {
public static void main(String[] args) throws Exception {
// 实例化对象
User user = new User();
// 获取Property描述器
PropertyDescriptor descriptor = new PropertyDescriptor("username", User.class);
// 获取setter方法(setUsername())
Method writeMethod = descriptor.getWriteMethod();
// set值(user.setUserName("carl"))
writeMethod.invoke(user, "carl");
// 获取getter方法(getUsername())
Method readMethod = descriptor.getReadMethod();
// 调用getter方法(getUsername())
Object result = readMethod.invoke(user);
System.out.println(result);
}
}
3) result
2、BeanInfo
我们可以使用更加灵活的方式来修改对象的值。那就是使用Introspector工具类。下面是具体实例:
/**
* Created by Carl on 2016/11/26.
*/
public class IntrospectorTest {
public static void main(String[] args) throws Exception {
User user = new User();
BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor propertyDescriptor : propertyDescriptors){
if("username".equals(propertyDescriptor.getName())){
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(user, "carl");
}
if("age".equals(propertyDescriptor.getName())){
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(user, 26);
}
}
System.out.println(user);
}
}
and result:
注意设置的值类型一定要与property的类型一样,不然就是会报错。上面的例子中如果把代码:
writeMethod.invoke(user, 26);
改成:
writeMethod.invoke(user, "26");
就会报以下的错误:
3、BeanUtils
作为一个对于代码有点小洁癖的人,如果需要设置的属性多了就会超级不爽。对于JDK的原始化码有优化工作的我们首先想到Java的好朋友apache.然后我们可以使用它的工具Jar包:commons-beanutils.jar,这个里面有对于内省操作的封装。
/**
* Created by Carl on 2016/11/26.
*/
public class BeanUtilsTest {
public static void main(String[] args) throws Exception {
User user = new User();
BeanUtils.setProperty(user, "username", "carl");
BeanUtils.setProperty(user, "age", 26);
System.out.println(user);
}
}
and reuslt:
4、Introspection or Reflect
1) 反射
相对而言,反射比内省更容易理解一点。用一句比较白的话来概括,反射就是让你可以通过名称来得到对象(类,属性,方法)的技术。例如我们可以通过类名来生成一个类的实例;知道了方法名,就可以调用这个方法;知道了属性名就可以访问这个属性的值。可以参看我之前的blog – Java JDK Reflect.
2) 内省
内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。
3) 总结
将Java的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的Struts,还有用于处理XML文件的Digester项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。