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()方法有三个参数:
- 类加载器(Class Loader)
- 需要实现的接口数组
- InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。
InvocationHandler接口
public class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// do something "dynamic"
}
}
invoke()方法同样有三个参数:
- 动态代理类的引用,通常情况下不需要它。但可以使用getClass()方法,得到proxy的Class类从而取得实例的类信息,如方法列表,annotation等。
- 方法的引用,代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等
- args对象数组,代表被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)
参考
- http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
- http://www.jianshu.com/p/2315dda64ad2
- https://github.com/JustinSDK/JavaSE6Tutorial/blob/master/docs/CH16.md
- https://www.ibm.com/developerworks/cn/java/j-dyn0603/
- http://www.cnblogs.com/techyc/p/3455950.html
- http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html
- http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy
- https://www.ibm.com/developerworks/java/library/j-jtp08305/index.html