java的reflection和introspector
- JAVA反射机制是在运行状态中,对于任意一个类,都能够得到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 用一句比较白的话来概括,反射就是让你可以通过名称来得到对象 ( 类,属性,方法 ) 的技术。例如我们可以通过类名来生成一个类的实例;知道了方法名,就可以调用这个方法;知道了属性名就可以访问这个属性的值。
- Java反射机制主要提供了以下功能:
- 1、为一个类生成对应的Class对象
- 运用(已知对象) getClass():Object类中的方法,每个类都拥有此方法。
- 如:String str=new String();Class strClass=str.getClass();
- 运用(已知子类的class) Class.getSuperclass():Class类中的方法,返回该Class的父类的Class;
- 运用(已知类全名)Class.forName()静态方法
- 运用(已知类)类名.class
- 2、通过类名来构造一个类的实例
- a、调用无参的构造函数:
- Class newoneClass = Class.forName(类全名);
- newoneClass.newInstance();
- b、调用有参的构造函数:我们可以自定义一个函数。
- public Object newInstance(String className, Object[] args) throws Exception {
- //args为参数数组
- Class newoneClass = Class.forName(className);
- //得到参数的Class数组(每个参数的class组成的数组),由此来决定调用那个构造函数
- Class[] argsClass = new Class[args.length];
- for (int i = 0, j = args.length; i < j; i++) {
- argsClass[i] = args[i].getClass();
- }
- Constructor cons = newoneClass.getConstructor(argsClass); //根据argsClass选择函数
- return cons.newInstance(args); //根据具体参数实例化对象。
- }
- 3、得到某个对象的属性
- a、非静态属性:首先得到class,然后得到这个class具有的field,然后以具体实例为参数
- 调用这个field
- public Object getProperty(Object owner, String fieldName) throws Exception {
- Class ownerClass = owner.getClass();//首先得到class
- Field field = ownerClass.getField(fieldName);
- //然后得到这个class具有的field,也可以通过getFields()得到所有的field
- Object property = field.get(owner);
- //owner指出了取得那个实例的这个属性值,
- 如果这个属性是非公有的,这里会报IllegalAccessException。
- return property;
- }
- b、静态属性:
- 只有最后一步不同,由于静态属性属于这个类,所以只要在这个类上调用这个field即可
- Object property = field.get(ownerClass);
- 4、执行某对象的方法
- public Object invokeMethod(Object owner, String methodName,
- Object[] args) throws Exception {
- Class ownerClass = owner.getClass(); //也是从class开始的
- //得到参数的class数组,相当于得到参数列表的类型数组,来取决我们选择哪个函数。
- Class[] argsClass = new Class[args.length];
- for (int i = 0, j = args.length; i < j; i++) {
- argsClass[i] = args[i].getClass();
- }
- //根据函数名和函数类型来选择函数
- Method method = ownerClass.getMethod(methodName, argsClass);
- return method.invoke(owner, args);//具体实例下,具体参数值下调用此函数
- }
- 5、执行类的静态方法
- 和上面的相似只是最后一行不需要指定具体实例
- return method.invoke(null, args);
- 6、判断是否为某个类的实例
- public boolean isInstance(Object obj, Class cls) {
- return cls.isInstance(obj);
- }
内省
- /*
- * Created on 2004-6-29
- */
- package demo;
- import java.beans.BeanInfo;
- import java.beans.Introspector;
- import java.beans.PropertyDescriptor;
- /**
- * 内省演示例子
- * @author liudong
- */
- public class IntrospectorDemo {
- String name;
- public static void main(String[] args) throws Exception{
- IntrospectorDemo demo = new IntrospectorDemo();
- demo.setName("Winter Lau");
- //如果不想把父类的属性也列出来的话,
- //那getBeanInfo的第二个参数填写父类的信息
- BeanInfo bi = Introspector.getBeanInfo(demo.getClass(),Object.class);
- PropertyDescriptor[] props = bi.getPropertyDescriptors();
- for(int i=0;i<props.length;i++){
- System.out.println(props[i].getName()+"="+props[i].getReadMethod().invoke(demo,null));
- }
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Web开发框架Struts中的FormBean就是通过内省机制来将表单中的数据映射到类的属性上,因此要求FormBean的每个属性要有getter/setter方法。但也并不总是这样,什么意思呢?就是说对一个Bean类来讲,我可以没有属性,但是只要有getter/setter方法中的其中一个,那么Java的内省机制就会认为存在一个属性,比如类中有方法setMobile,那么就认为存在一个mobile的属性,这样可以方便我们把Bean类通过一个接口来定义而不用去关系具体实现,不用去关系Bean中数据的存储。比如我们可以把所有的getter/setter方法放到接口里定义,但是真正数据的存取则是在具体类中去实现,这样可提高系统的扩展性。
总结
将Java的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的Struts,还有用于处理XML文件的Digester项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性
一些概念:
内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。
JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。
例如类UserInfo :

- package com.peidasoft.Introspector;
- public class UserInfo {
- private long userId;
- private String userName;
- private int age;
- private String emailAddress;
- public long getUserId() {
- return userId;
- }
- public void setUserId(long userId) {
- this.userId = userId;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getEmailAddress() {
- return emailAddress;
- }
- public void setEmailAddress(String emailAddress) {
- this.emailAddress = emailAddress;
- }
- }

在类UserInfo中有属性 userName, 那我们可以通过 getUserName,setUserName来得到其值或者设置新的值。通过getUserName/setUserName来访问 userName属性,这就是默认的规则。 Java JDK中提供了一套 API 用来访问某个属性的 getter/setter 方法,这就是内省。
JDK内省类库:
PropertyDescriptor类:
PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:
1. getPropertyType(),获得属性的Class对象;
2. getReadMethod(),获得用于读取属性值的方法;getWriteMethod(),获得用于写入属性值的方法;
3. hashCode(),获取对象的哈希值;
4. setReadMethod(Method readMethod),设置用于读取属性值的方法;
5. setWriteMethod(Method writeMethod),设置用于写入属性值的方法。
实例代码如下:

- package com.peidasoft.Introspector;
- import java.beans.BeanInfo;
- import java.beans.Introspector;
- import java.beans.PropertyDescriptor;
- import java.lang.reflect.Method;
- public class BeanInfoUtil {
- public static void setProperty(UserInfo userInfo,String userName)throws Exception{
- PropertyDescriptor propDesc=new PropertyDescriptor(userName,UserInfo.class);
- Method methodSetUserName=propDesc.getWriteMethod();
- methodSetUserName.invoke(userInfo, "wong");
- System.out.println("set userName:"+userInfo.getUserName());
- }
- public static void getProperty(UserInfo userInfo,String userName)throws Exception{
- PropertyDescriptor proDescriptor =new PropertyDescriptor(userName,UserInfo.class);
- Method methodGetUserName=proDescriptor.getReadMethod();
- Object objUserName=methodGetUserName.invoke(userInfo);
- System.out.println("get userName:"+objUserName.toString());
- }
- }

Introspector类:
将JavaBean中的属性封装起来进行操作。在程序把一个类当做JavaBean来看,就是调用Introspector.getBeanInfo()方法,得到的BeanInfo对象封装了把这个类当做JavaBean看的结果信息,即属性的信息。
getPropertyDescriptors(),获得属性的描述,可以采用遍历BeanInfo的方法,来查找、设置类的属性。具体代码如下:

- package com.peidasoft.Introspector;
- import java.beans.BeanInfo;
- import java.beans.Introspector;
- import java.beans.PropertyDescriptor;
- import java.lang.reflect.Method;
- public class BeanInfoUtil {
- public static void setPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
- BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
- PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
- if(proDescrtptors!=null&&proDescrtptors.length>0){
- for(PropertyDescriptor propDesc:proDescrtptors){
- if(propDesc.getName().equals(userName)){
- Method methodSetUserName=propDesc.getWriteMethod();
- methodSetUserName.invoke(userInfo, "alan");
- System.out.println("set userName:"+userInfo.getUserName());
- break;
- }
- }
- }
- }
- public static void getPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
- BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
- PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
- if(proDescrtptors!=null&&proDescrtptors.length>0){
- for(PropertyDescriptor propDesc:proDescrtptors){
- if(propDesc.getName().equals(userName)){
- Method methodGetUserName=propDesc.getReadMethod();
- Object objUserName=methodGetUserName.invoke(userInfo);
- System.out.println("get userName:"+objUserName.toString());
- break;
- }
- }
- }
- }
- }

通过这两个类的比较可以看出,都是需要获得PropertyDescriptor,只是方式不一样:前者通过创建对象直接获得,后者需要遍历,所以使用PropertyDescriptor类更加方便。
使用实例:

- package com.peidasoft.Introspector;
- public class BeanInfoTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- UserInfo userInfo=new UserInfo();
- userInfo.setUserName("peida");
- try {
- BeanInfoUtil.getProperty(userInfo, "userName");
- BeanInfoUtil.setProperty(userInfo, "userName");
- BeanInfoUtil.getProperty(userInfo, "userName");
- BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");
- BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
- BeanInfoUtil.setProperty(userInfo, "age");
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }

输出:

- get userName:peida
- set userName:wong
- get userName:wong
- set userName:alan
- get userName:alan
- java.lang.IllegalArgumentException: argument type mismatch
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
- at java.lang.reflect.Method.invoke(Method.java:597)
- at com.peidasoft.Introspector.BeanInfoUtil.setProperty(BeanInfoUtil.java:14)
- at com.peidasoft.Introspector.BeanInfoTest.main(BeanInfoTest.java:22)

说明:BeanInfoUtil.setProperty(userInfo, "age");报错是应为age属性是int数据类型,而setProperty方法里面默认给age属性赋的值是String类型。所以会爆出argument type mismatch参数类型不匹配的错误信息。
BeanUtils工具包:
由上述可看出,内省操作非常的繁琐,所以所以Apache开发了一套简单、易用的API来操作Bean的属性——BeanUtils工具包。
BeanUtils工具包:下载:http://commons.apache.org/beanutils/ 注意:应用的时候还需要一个logging包 http://commons.apache.org/logging/
使用BeanUtils工具包完成上面的测试代码:

- package com.peidasoft.Beanutil;
- import java.lang.reflect.InvocationTargetException;
- import org.apache.commons.beanutils.BeanUtils;
- import org.apache.commons.beanutils.PropertyUtils;
- import com.peidasoft.Introspector.UserInfo;
- public class BeanUtilTest {
- public static void main(String[] args) {
- UserInfo userInfo=new UserInfo();
- try {
- BeanUtils.setProperty(userInfo, "userName", "peida");
- System.out.println("set userName:"+userInfo.getUserName());
- System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
- BeanUtils.setProperty(userInfo, "age", 18);
- System.out.println("set age:"+userInfo.getAge());
- System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
- System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
- System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
- PropertyUtils.setProperty(userInfo, "age", 8);
- System.out.println(PropertyUtils.getProperty(userInfo, "age"));
- System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
- PropertyUtils.setProperty(userInfo, "age", "8");
- }
- catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- catch (NoSuchMethodException e) {
- e.printStackTrace();
- }
- }
- }

运行结果:

- set userName:peida
- get userName:peida
- set age:18
- get age:18
- get userName type:java.lang.String
- get age type:java.lang.String
- 8
- java.lang.Integer
- Exception in thread "main" java.lang.IllegalArgumentException: Cannot invoke com.peidasoft.Introspector.UserInfo.setAge
- on bean class 'class com.peidasoft.Introspector.UserInfo' - argument type mismatch - had objects of type "java.lang.String"
- but expected signature "int"
- at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2235)
- at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:2151)
- at org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1957)
- at org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:2064)
- at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:858)
- at com.peidasoft.orm.Beanutil.BeanUtilTest.main(BeanUtilTest.java:38)
- Caused by: java.lang.IllegalArgumentException: argument type mismatch
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
- at java.lang.reflect.Method.invoke(Method.java:597)
- at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2170)
- ... 5 more

说明:
1.获得属性的值,例如,BeanUtils.getProperty(userInfo,"userName"),返回字符串
2.设置属性的值,例如,BeanUtils.setProperty(userInfo,"age",8),参数是字符串或基本类型自动包装。设置属性的值是字符串,获得的值也是字符串,不是基本类型。 3.BeanUtils的特点:
1). 对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成。
2). 对引用数据类型的属性的操作:首先在类中必须有对象,不能是null,例如,private Date birthday=new Date();。操作的是对象的属性而不是整个对象,例如,BeanUtils.setProperty(userInfo,"birthday.time",111111);

- package com.peidasoft.Introspector;
- import java.util.Date;
- public class UserInfo {
- private Date birthday = new Date();
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
- public Date getBirthday() {
- return birthday;
- }
- }


- package com.peidasoft.Beanutil;
- import java.lang.reflect.InvocationTargetException;
- import org.apache.commons.beanutils.BeanUtils;
- import com.peidasoft.Introspector.UserInfo;
- public class BeanUtilTest {
- public static void main(String[] args) {
- UserInfo userInfo=new UserInfo();
- try {
- BeanUtils.setProperty(userInfo, "birthday.time","111111");
- Object obj = BeanUtils.getProperty(userInfo, "birthday.time");
- System.out.println(obj);
- }
- catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- catch (NoSuchMethodException e) {
- e.printStackTrace();
- }
- }
- }

3.PropertyUtils类和BeanUtils不同在于,运行getProperty、setProperty操作时,没有类型转换,使用属性的原有类型或者包装类。由于age属性的数据类型是int,所以方法PropertyUtils.setProperty(userInfo, "age", "8")会爆出数据类型不匹配,无法将值赋给属性。
参考资料:
1.http://www.cnblogs.com/avenwu/archive/2012/02/28/2372586.html
2.http://blog.csdn.net/zhuruoyun/article/details/8219333
java的reflection和introspector的更多相关文章
- [Java Basics] Reflection
For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.C ...
- java的内省(introspector)
package com.wzh.test.introspector; import java.beans.BeanInfo; import java.beans.IntrospectionExcept ...
- java.lang.reflection打印一个类的全部信息
package com.ljy.chapter5; import java.lang.reflect.Constructor; import java.lang.reflect.Field; impo ...
- Java的Reflection机制
什么时候使用Reflection: 在java语言中,创建一个类的对象通常使用new operator,但是如果预先不知道Class的名字,类名是在程序运行过程中通过参数传递过来,就没法使用这种方法了 ...
- java学习--Reflection反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- java的reflection
Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性.例如,使用它能获得 Java 类中 ...
- Java之reflection(反射机制)——通过反射操作泛型,注解
一.反射操作泛型(Generic) Java采用泛型擦除机制来引入泛型.Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦.但是编译一旦完成,所有和泛型有关的类型 ...
- Java:reflection
参考:http://docs.oracle.com/javase/tutorial/reflect/index.html what and why? 通过反射来检测或者修改应用某些对象在运行时的状态或 ...
- 公共技术点( Java 反射 Reflection)
转载路径:http://p.codekk.com/blogs/detail/5596953ed6459ae7934997c5 本文为 Android 开源项目源码解析 公共技术点中的 Java 反射 ...
随机推荐
- 数据库系统概论学习2-《关系数据库与 E/R 模型》
-----------------------------一直更新学习内容------------------------------------ 建立一个关系数据库需要几步? 2.关系数据库与 E/ ...
- ScreenOS地址转换
目录 1. NAT-src 1.1 来自DIP池(启用PAT)的NAT-src 1.2 来自DIP池(禁用PAT)的NAT-src 1.3 来自DIP池(带有地址变换)的NAT-src 1.4 来自出 ...
- JSP--内置对象&动作标签介绍
1.JSP中常用的9大内置对象? 内置对象:在JSP页面中能直接使用的对象就是JSP内置对象,事实上,JSP底层就是一个java类,可以在JSP中直接使用的,必然存在JSP翻译后的java类 下面简单 ...
- 关于centos7下/etc/sysconfig/目录没有iptables问题
在新买的centos7服务器中想打开防火墙,采用传统centos6的方式用service iptables restart/stop/status 之后报错: 而在/etc/sysconfig/目录下 ...
- sql server update时,是行锁还是表锁
https://bbs.csdn.net/topics/120000749 http://www.cnblogs.com/s021368/articles/2148659.html 问题: udpat ...
- 【P1714】切蛋糕(单调队列)
实在不明白难度等级,难不成前缀和是个很变态的东西? 说白了就是单调队列裸题,都没加什么别的东西,就是一个前缀和的计算,然而这个题也不是要用它优化,而是必须这么做啊. #include<iostr ...
- 【bzoj5055】膜法师(离散化+树状数组)
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=5055 这道题……不得不说,从标题到题面都能看出一股浓浓的膜法气息……苟…… 题意就是统计顺序 ...
- java 反射机制复习笔记。
1.java 反射的作用(好处): (1)能够使我们很方便的创建灵活的代码,这些代码可以在运行时装配(运行时获取一个类实例),就不用在组件之间进行源代码链接了,大大提高系统的灵活性和扩展性. (2)与 ...
- 【转】nodejs mysql 链接数据库集群
1.建立数据库连接:createConnection(Object)方法 该方法接受一个对象作为参数,该对象有四个常用的属性host,user,password,database.与php中 ...
- JAVA8新特性——方法引用
JAVA9都要出来了,JAVA8新特性都没搞清楚,是不是有点掉队哦~ 在Lamda新特性的支持下,JAVA8中可以使用lamda表达式来创建匿名方法.然而,有时候我们仅仅是需要调用一个已存在的方法(如 ...