JAVA中的反射机制详解

JAVA反射机制 

  JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 

  Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。 

  1. 得到某个对象的属性 

  1 public Object getProperty(Object owner, String fieldName) throws Exception { 

  2 Class ownerClass = owner.getClass(); 

  3 

  4 Field field = ownerClass.getField(fieldName); 

  5 

  6 Object property = field.get(owner); 

  7 

  8 return property; 

  9 } 

  Class ownerClass = owner.getClass():得到该对象的Class。 

  Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。 

  Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。 

  2. 得到某个类的静态属性 

  1 public Object getStaticProperty(String className, String fieldName) 

  2 throws Exception { 

  3 Class ownerClass = Class.forName(className); 

  4 

  5 Field field = ownerClass.getField(fieldName); 

  6 

  7 Object property = field.get(ownerClass); 

  8 

  9 return property; 

  10 } 

  Class ownerClass = Class.forName(className) :首先得到这个类的Class。 

  Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。 

  Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。 

  3. 执行某对象的方法 

  1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception { 

  2 

  3 Class ownerClass = owner.getClass(); 

  4 

  5 Class[] argsClass = new Class[args.length]; 

  6 

  7 for (int i = 0, j = args.length; i < j; i++) { 

  8 argsClass[i] = args[i].getClass(); 

  9 } 

  10 

  11 Method method = ownerClass.getMethod(methodName, argsClass); 

  12 

  13 return method.invoke(owner, args); 

  14 } 

  Class owner_class = owner.getClass() :首先还是必须得到这个对象的Class。 

  5~9行:配置参数的Class数组,作为寻找Method的条件。 

  Method method = ownerClass.getMethod(methodName, argsClass):通过Method名和参数的Class数组得到要执行的Method。 

  method.invoke(owner, args):执行该Method,invoke方法的参数是执行这个方法的对象,和参数数组。返回值是Object,也既是该方法的返回值。 

  4. 执行某个类的静态方法 

  1 public Object invokeStaticMethod(String className, String methodName, 

  2 Object[] args) throws Exception { 

  3 Class ownerClass = Class.forName(className); 

  4 

  5 Class[] argsClass = new Class[args.length]; 

  6 

  7 for (int i = 0, j = args.length; i < j; i++) { 

  8 argsClass[i] = args[i].getClass(); 

  9 } 

  10 

  11 Method method = ownerClass.getMethod(methodName, argsClass); 

  12 

  13 return method.invoke(null, args); 

  14 } 

  基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。 

  5. 新建实例 

  1 

  2 public Object newInstance(String className, Object[] args) throws Exception { 

  3 Class newoneClass = Class.forName(className); 

  4 

  5 Class[] argsClass = new Class[args.length]; 

  6 

  7 for (int i = 0, j = args.length; i < j; i++) { 

  8 argsClass[i] = args[i].getClass(); 

  9 } 

  10 

  11 Constructor cons = newoneClass.getConstructor(argsClass); 

  12 

  13 return cons.newInstance(args); 

  14 

  15 } 

  这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。 

  Class newoneClass = Class.forName(className):第一步,得到要构造的实例的Class。 

  第5~第9行:得到参数的Class数组。 

  Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。 

  cons.newInstance(args):新建实例。 

  6. 判断是否为某个类的实例 

  1 public boolean isInstance(Object obj, Class cls) { 

  2 return cls.isInstance(obj); 

  3 } 

  7. 得到数组中的某个元素 

  1 public Object getByArray(Object array, int index) { 

  2 return Array.get(array,index); 

  3 }

 

 

关于Java反射机制的一个实例

JSP的规范中,有个表达式语言(Expression Language, 简称EL),可以算是一个微型的语言,其中对request, page, session, application中预存的JavaBean对象的引用方式很是简单。最近正好需要写一个支持简单EL的taglib,所以就研究了下Java反射机制,目前基本上实现了多级bean的属性的访问,经测试,还是可以用的。如:

 
  1. public static void main(String[] args){  
  2.     UserBean bean = new UserBean();  
  3.     bean.setName("John Abruzzi");  
  4.     bean.setNick("Abruzzi");  
  5.     bean.setAge(24);  
  6.       
  7.     AddressBean addr = new AddressBean();  
  8.     addr.setZip("0086");  
  9.     addr.setStreet("Bell Street #12");  
  10.     bean.setAddress(addr);  
  11.       
  12.     System.out.println(BeanParser.doParse(bean, "bean.address.street"));  
  13.     System.out.println(BeanParser.doParse(bean, "bean.address.zip"));  
  14.     System.out.println(BeanParser.doParse(bean, "bean.name"));  
  15.     System.out.println(BeanParser.doParse(bean, "bean.nick"));  
  16.     System.out.println(BeanParser.doParse(bean, "bean.age"));  

需要可以输出:

 
  1. Bell Street #12 
  2. 0086 
  3. John Abruzzi  
  4. Abruzzi  
  5. 24 

反射,即由一个抽象的对象(如Object),取出这个具体对象的属性或者方法(就EL中关于Bean的引用来说,这个定义已经够了)。在EL中,对一个Bean的某字段进行引用,只需 ${bean.field},当然,这个bean是已经被set到容器中的,这就是Java反射机制。

我们从容器中取出以bean为名字的Object,通过Java反射机制知道它的真实类型,然后通过field以javabean规范拼出方法名,进行调用,如果这个表达式是多级的,如${bean.field.field},其中第一个field本身就是一个bean对象,同样需要递归的进行解析。

大概原理就是这些了,看代码吧:

现有一个UserBean, 其中的一个字段Address本身又是一个AddressBean。

 
  1. package elparser;  
  2.  
  3. public class AddressBean {  
  4.     private String street;  
  5.     private String zip;  
  6.       
  7.     public String getZip() {  
  8.         return zip;  
  9.     }  
  10.  
  11.     public void setZip(String zip) {  
  12.         this.zip = zip;  
  13.     }  
  14.  
  15.     public String getStreet() {  
  16.         return street;  
  17.     }  
  18.  
  19.     public void setStreet(String street) {  
  20.         this.street = street;  
  21.     }  
  22.       

然后是UserBean

 
  1. package elparser;  
  2.  
  3. public class UserBean {  
  4.     private String name;  
  5.     private String nick;  
  6.     private AddressBean address;  
  7.     private int age;  
  8.       
  9.     public int getAge(){  
  10.         return this.age;  
  11.     }  
  12.       
  13.     public void setAge(int age){  
  14.         this.age = age;  
  15.     }  
  16.       
  17.     public String getName() {  
  18.         return name;  
  19.     }  
  20.     public void setName(String name) {  
  21.         this.name = name;  
  22.     }  
  23.     public String getNick() {  
  24.         return nick;  
  25.     }  
  26.     public void setNick(String nick) {  
  27.         this.nick = nick;  
  28.     }  
  29.     public AddressBean getAddress() {  
  30.         return address;  
  31.     }  
  32.     public void setAddress(AddressBean address) {  
  33.         this.address = address;  
  34.     }  

Bean都是很简单的,考虑到对基本类型的支持,所以在UserBean中加入一个int型的字段age

好了,看看怎么通过一个串和一个对象来取出其中的字段来:

 
  1. package elparser;  
  2.  
  3. import java.lang.reflect.Method;  
  4.  
  5. public class BeanParser {  
  6.       
  7.     private static String getMethodName(String property, String prefix){  
  8.         String prop = Character.toUpperCase(property.charAt(0))+property.substring(1);  
  9.       
  10.         String methodName = prefix + prop;  
  11.       
  12.         return methodName;  
  13.     }  
  14.       
  15.     private static Object parse(Object bean, String expr){  
  16.         Class beanClass = bean.getClass();  
  17.           
  18.         Method method = null;  
  19.         Object result = null;  
  20.           
  21.         try{  
  22.             //这两步是关键,get方法不需要传入参数,所以只是new出两个空数组传入  
  23.             method = beanClass.getMethod(getMethodName(expr, "get"), new Class[]{});  
  24.             result = method.invoke(bean, new Object[]{});  
  25.         }catch(Exception e){  
  26.             System.out.println(e.getMessage());  
  27.         }  
  28.           
  29.         return result;  
  30.     }  
  31.       
  32.     public static Object doParse(Object bean, String expr){  
  33.         String keys[] = expr.split("\\.");  
  34.           
  35.         Object obj = null;  
  36.           
  37.         for(int i = 1; i < keys.length;i++){  
  38.             obj = parse(bean, keys[i]);  
  39.             bean = obj;  
  40.         }//递归parse  
  41.           
  42.         return obj;  
  43.     }  
  44.       
  45.     public static void main(String[] args){  
  46.         UserBean bean = new UserBean();  
  47.         bean.setName("John Abruzzi");  
  48.         bean.setNick("Abruzzi");  
  49.         bean.setAge(24);  
  50.           
  51.         AddressBean addr = new AddressBean();  
  52.         addr.setZip("0086");  
  53.         addr.setStreet("Bell Street #12");  
  54.         bean.setAddress(addr);  
  55.           
  56.         System.out.println(BeanParser.doParse(bean, "bean.address.street"));  
  57.         System.out.println(BeanParser.doParse(bean, "bean.address.zip"));  
  58.         System.out.println(BeanParser.doParse(bean, "bean.name"));  
  59.         System.out.println(BeanParser.doParse(bean, "bean.nick"));  
  60.         System.out.println(BeanParser.doParse(bean, "bean.age"));  
  61.     }  

代码比较简短,重要部分有注释,应该很容易理解。当然这篇文章主要是关于Java的反射机制,如果需要对EL完全支持,可以使用JavaCC做一个简单的分析器(Apache的commons中包含一个el的项目,就是用javacc写的分析器)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值