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的属性的访问,经测试,还是可以用的。如:
- public static void main(String[] args){
- UserBean bean = new UserBean();
- bean.setName("John Abruzzi");
- bean.setNick("Abruzzi");
- bean.setAge(24);
- AddressBean addr = new AddressBean();
- addr.setZip("0086");
- addr.setStreet("Bell Street #12");
- bean.setAddress(addr);
- System.out.println(BeanParser.doParse(bean, "bean.address.street"));
- System.out.println(BeanParser.doParse(bean, "bean.address.zip"));
- System.out.println(BeanParser.doParse(bean, "bean.name"));
- System.out.println(BeanParser.doParse(bean, "bean.nick"));
- System.out.println(BeanParser.doParse(bean, "bean.age"));
- }
需要可以输出:
- Bell Street #12
- 0086
- John Abruzzi
- Abruzzi
- 24
反射,即由一个抽象的对象(如Object),取出这个具体对象的属性或者方法(就EL中关于Bean的引用来说,这个定义已经够了)。在EL中,对一个Bean的某字段进行引用,只需 ${bean.field},当然,这个bean是已经被set到容器中的,这就是Java反射机制。
我们从容器中取出以bean为名字的Object,通过Java反射机制知道它的真实类型,然后通过field以javabean规范拼出方法名,进行调用,如果这个表达式是多级的,如${bean.field.field},其中第一个field本身就是一个bean对象,同样需要递归的进行解析。
大概原理就是这些了,看代码吧:
现有一个UserBean, 其中的一个字段Address本身又是一个AddressBean。
- package elparser;
- public class AddressBean {
- private String street;
- private String zip;
- public String getZip() {
- return zip;
- }
- public void setZip(String zip) {
- this.zip = zip;
- }
- public String getStreet() {
- return street;
- }
- public void setStreet(String street) {
- this.street = street;
- }
- }
然后是UserBean
- package elparser;
- public class UserBean {
- private String name;
- private String nick;
- private AddressBean address;
- private int age;
- public int getAge(){
- return this.age;
- }
- public void setAge(int age){
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getNick() {
- return nick;
- }
- public void setNick(String nick) {
- this.nick = nick;
- }
- public AddressBean getAddress() {
- return address;
- }
- public void setAddress(AddressBean address) {
- this.address = address;
- }
- }
Bean都是很简单的,考虑到对基本类型的支持,所以在UserBean中加入一个int型的字段age
好了,看看怎么通过一个串和一个对象来取出其中的字段来:
- package elparser;
- import java.lang.reflect.Method;
- public class BeanParser {
- private static String getMethodName(String property, String prefix){
- String prop = Character.toUpperCase(property.charAt(0))+property.substring(1);
- String methodName = prefix + prop;
- return methodName;
- }
- private static Object parse(Object bean, String expr){
- Class beanClass = bean.getClass();
- Method method = null;
- Object result = null;
- try{
- //这两步是关键,get方法不需要传入参数,所以只是new出两个空数组传入
- method = beanClass.getMethod(getMethodName(expr, "get"), new Class[]{});
- result = method.invoke(bean, new Object[]{});
- }catch(Exception e){
- System.out.println(e.getMessage());
- }
- return result;
- }
- public static Object doParse(Object bean, String expr){
- String keys[] = expr.split("\\.");
- Object obj = null;
- for(int i = 1; i < keys.length;i++){
- obj = parse(bean, keys[i]);
- bean = obj;
- }//递归parse
- return obj;
- }
- public static void main(String[] args){
- UserBean bean = new UserBean();
- bean.setName("John Abruzzi");
- bean.setNick("Abruzzi");
- bean.setAge(24);
- AddressBean addr = new AddressBean();
- addr.setZip("0086");
- addr.setStreet("Bell Street #12");
- bean.setAddress(addr);
- System.out.println(BeanParser.doParse(bean, "bean.address.street"));
- System.out.println(BeanParser.doParse(bean, "bean.address.zip"));
- System.out.println(BeanParser.doParse(bean, "bean.name"));
- System.out.println(BeanParser.doParse(bean, "bean.nick"));
- System.out.println(BeanParser.doParse(bean, "bean.age"));
- }
- }
代码比较简短,重要部分有注释,应该很容易理解。当然这篇文章主要是关于Java的反射机制,如果需要对EL完全支持,可以使用JavaCC做一个简单的分析器(Apache的commons中包含一个el的项目,就是用javacc写的分析器)。