suninf 's blog

It’s not what you know, it’s how you think

Java反射与动态代理

Catalog

Java反射机制(Reflection)是区别于C++之类的语言的重要特征,能在运行时自省,操作类的属性,方法,以及创建等等。

动态代理,利用Java的反射技术,在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces)。

语言支持基础

Class类型

使用反射的基础是 java.lang.Class 实例

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    //...
    public static Class<?> forName(String className) 
        throws ClassNotFoundException;
    public T newInstance() 
        throws InstantiationException, IllegalAccessException;
    
    public native boolean isInstance(Object obj);
    public native boolean isAssignableFrom(Class<?> cls);
    public native boolean isInterface();
    public native boolean isArray();
    public native boolean isPrimitive();
    public native Class<?> getComponentType();

    public String getName()
    public ClassLoader getClassLoader()
    public native Class<? super T> getSuperclass();
    public Class<?>[] getInterfaces()

    public Class<?>[] getClasses()
    public Class<?>[] getDeclaredClasses() 
        throws SecurityException

    public Field[] getFields() 
        throws SecurityException
    public Field getField(String name)
    public Field[] getDeclaredFields() 
        throws SecurityException
    public Field getDeclaredField(String name)

    public Method[] getMethods() throws SecurityException
    public Method getMethod(String name, Class<?>... parameterTypes) 
        throws NoSuchMethodException, SecurityException
    public Method[] getDeclaredMethods() 
        throws SecurityException
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
        throws NoSuchMethodException, SecurityException

    public Constructor<?>[] getConstructors() 
        throws SecurityException
    public Constructor<T> getConstructor(Class<?>... parameterTypes) 
        throws NoSuchMethodException, SecurityException
    public Constructor<?>[] getDeclaredConstructors() 
        throws SecurityException
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
        throws NoSuchMethodException, SecurityException
}
  • 简单的例子
Class clas = null;
try {
    clas = Class.forName(name);
} catch (ClassNotFoundException ex) {
    // handle exception case
}
  • 关于方法:Object newInstance()

Class支持使用缺省函数创建新的实例,即使这种方法只允许您使用一种特殊的构造函数,如果这正是您需要的,那么它将提供一种非常方便的快捷方式。当与JavaBeans协作时这项技术尤其有用,JavaBeans需要定义公共、无参数构造函数。

类型的class属性

类型获取其Class的实例的方式:

Class clas = MyClass.class;

Object的getClass方法

返回对象的运行时类型

public final native Class<?> getClass();

instanceof 运算符

instanceof是Java的一个二元操作符,测试它左边的对象是否是它右边的类的实例,如:

String s = "I AM an Object!";
boolean isObject = s instanceof Object;

反射

整理java.lang.reflect库的一些常用类型

构造函数 Constructor

常用方法:

public String getName()
public int getModifiers()
public Class<?>[] getParameterTypes()
public int getParameterCount()
public T newInstance(Object ... initargs)

比如:

public class TwoString {
    private String m_s1, m_s2;
    public TwoString(String s1, String s2) {
        m_s1 = s1;
        m_s2 = s2;
    }
}

// ...
Class[] types = new Class[] { String.class, String.class };

Constructor cons = TwoString.class.getConstructor(types);
Object[] args = new Object[] { "a", "b" };
TwoString ts = cons.newInstance(args);

字段 Field

常用方法:

public String getName()
public int getModifiers()

public Object get(Object obj)
public boolean getBoolean(Object obj)
//getXXX ...

public void set(Object obj, Object value)
    throws IllegalArgumentException, IllegalAccessException
public void setBoolean(Object obj, boolean z)
    throws IllegalArgumentException, IllegalAccessException
// setXXX ...

比如:给一个对象的指定字段的int值加1

public class MyTest {
    private int yeah = 5;
    public int getYeah() {return yeah;}
    public void setYeah(int yeah) { this.yeah = yeah; }
}

public static int incrementField(String name, Object obj) {
    try {
        Field field = obj.getClass().getDeclaredField(name);
        int value = field.getInt(obj) + 1;
        field.setInt(obj, value);
        return value;

    } catch (java.lang.NoSuchFieldException ex) {
    } catch (java.lang.IllegalAccessException ex) {
    }

    return 0;
}

测试:MyTest有一个int型的yeah字段,把该字段+1

MyTest mt = new MyTest();
incrementField("yeah", mt);

方法 Method

常用方法:

public String getName()
public int getModifiers()
public Class<?> getReturnType()
public Class<?>[] getParameterTypes()
public int getParameterCount()
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,

例子:通过get/set方法设置属性

public static int incrementProperty(String name, Object obj) {
    String prop = Character.toUpperCase(name.charAt(0)) +
        name.substring(1);
    String mname = "get" + prop;
    Class[] types = new Class[] {};

    try {
        Method method = obj.getClass().getMethod(mname, types);
        Object result = method.invoke(obj, new Object[0]);
        int value = ((Integer)result).intValue() + 1;

        mname = "set" + prop;
        types = new Class[] {int.class};
        method = obj.getClass().getMethod(mname, types);
        method.invoke(obj, new Object[] {new Integer(value)});
        return value;
    } catch (java.lang.NoSuchMethodException ex) {
    } catch (java.lang.reflect.InvocationTargetException ex) {
    } catch (java.lang.IllegalAccessException ex) {
    }

    return 0;
}

反射数组 Array

数组是Java编程语言中的对象。与所有对象一样,它们都有类。如果有一个数组,使用标准 getClass 方法,可以获得该数组的类。

数组的特殊处理使用 java.lang.reflect.Array 类提供的静态方法的集合。该类中的方法使您能够创建新数组,获得数组对象的长度,读和写数组对象的索引值。

public static Object growArray(Object array, int size) {
    Class type = array.getClass().getComponentType();
    Object grown = Array.newInstance(type, size);
    System.arraycopy(array, 0, grown, 0,
        Math.min(Array.getLength(array), size));
    return grown;
}

// test
int[] dary = {1,2,3};
growArray(dary, 5);

反射安全性

Java编程语言定义一种多级别方法来处理反射的安全性。

基本模式是对反射实施与应用于源代码接入相同的的限制:

  • 从任意位置到类公共组件的接入
  • 类自身外部无任何到私有组件的接入
  • 受保护和打包(缺省接入)组件的有限接入

Constructor 、 Field 和 Method 类都扩展了一个普通的基本类 java.lang.reflect.AccessibleObject 类。

该类定义一种 setAccessible 方法,使您能够启动或关闭对这些类中其中一个类的实例的接入检测。

public void setAccessible(boolean flag) throws SecurityException

另外,java支持JVM参数 -Djava.security.manager 以实现安全性管理器

关于反射的使用说明

  • 反射提高了代码的灵活性,主要用于框架的开发,但是也会增加代码的维护成本,因为代码逻辑会变得不直观
  • 反射对性能也有影响,所以一般情况下使用不多

动态代理

解决特定问题:一个接口的实现在编译时无法知道,需要在运行时才能实现

常用于实现一些自动化功能的注解,AOP等框架(如AOP in Spring),相关例子可以在Java注解看下。

创建动态代理

利用Java的Proxy类,调用Proxy.newProxyInstance()创建动态对象。

InvocationHandler handler = new MyInvocationHandler(...);
IFoo f = (IFoo) Proxy.newProxyInstance(IFoo.class.getClassLoader(),
                                     new Class[] { IFoo.class },
                                     handler);

Proxy.newProxyInstance()方法有三个参数:

  1. 类加载器(Class Loader)
  2. 需要实现的接口数组
  3. InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。

InvocationHandler接口

public class MyInvocationHandler implements InvocationHandler{

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // do something "dynamic"
  }
}

invoke()方法同样有三个参数:

  1. 动态代理类的引用,通常情况下不需要它。但可以使用getClass()方法,得到proxy的Class类从而取得实例的类信息,如方法列表,annotation等。
  2. 方法的引用,代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等
  3. args对象数组,代表被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)

参考

Comments