java中增强一个类的功能一般来说有以下几种方式:继承,包装,代理。而代理又分为静态代理和动态代理。本文主要讲的是动态代理。JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
首先代码了解代理类的结构:
//创建动态代理类
Class<?> clz = Proxy.getProxyClass(ProxyTest.class.getClassLoader(), Collection.class);
//代理类的类名
System.out.println(clz.getName());//$Proxy0
//代理类的父类
System.out.println(clz .getSuperclass().getSimpleName());//Proxy
//代理类实现的接口
Class<?>[] clzs = clz.getInterfaces();
for(Class<?> c:clzs){
System.out.println(c.getSimpleName());//Collection,这个结果说明jvm只能给具有相同接口的目标类进行代理
}
/**
* 代理类的成员变量的访问修饰符,类型,变量名(访问修饰符的值为10,代表其是private static修饰的,读者可调用Modifier类中的相应方法进行验证)
* Modifier.isPrivate(field.getModifiers());
*/
Field[] fields = clz.getDeclaredFields();
for(Field field:fields){
/**
* 输出结果为:10 Method m0、10 Method m1......10 Method m15
* 总共16个,笔者认为应该是跟目标类的拥有方法是对应的(<span style="color:#ff0000;">此处有待考证</span>)
*/
System.out.println(field.getModifiers()+" "+field.getType().getSimpleName()+" "+field.getName());
}
//所有的方法
Method[] methods = clz.getDeclaredMethods();
StringBuilder sb = new StringBuilder();
for(Method method:methods){
//输出所有方法的访问修饰符,返回值,参数类型
sb.append(method.getModifiers()).append(" ").append(method.getReturnType().getName()).append(" ");
sb.append(method.getName()+"(");
Class<?>[] parameterTypes = method.getParameterTypes();
if(parameterTypes.length>0){
sb.append(parameterTypes[0].getSimpleName());
}
for(int i=1;i<parameterTypes.length;i++){
sb.append(","+parameterTypes[i].getSimpleName());
}
sb.append(")\r\n");
}
/** 17代表方法都是public final修饰的
* 17 boolean add(Object)
17 boolean equals(Object)
17 java.lang.String toString()
17 int hashCode()
17 void clear()
17 boolean contains(Object)
17 boolean isEmpty()
17 boolean addAll(Collection)
17 java.util.Iterator iterator()
17 int size()
17 [Ljava.lang.Object; toArray(Object[])
17 [Ljava.lang.Object; toArray()
17 boolean remove(Object)
17 boolean containsAll(Collection)
17 boolean removeAll(Collection)
17 boolean retainAll(Collection)
*/
System.out.println(sb.toString());
//所有的构造函数
Constructor<?>[] constructors = clz.getDeclaredConstructors();
sb = new StringBuilder();
for(Constructor<?> constructor:constructors){
sb.append(constructor.getModifiers()).append(" ").append(constructor.getName()).append("(");
Class<?>[] parameterTypes = constructor.getParameterTypes();
if(parameterTypes.length>0){
sb.append(parameterTypes[0].getSimpleName());
}
for(int i=1;i<parameterTypes.length;i++){
sb.append(","+parameterTypes[i].getSimpleName());
}
sb.append(")\r\n");
}
/**
* 1 $Proxy0(InvocationHandler)
*/
System.out.println(sb.toString());
以上了解到jvm生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。动态类中只有一个public修饰的参数为InvocationHandler的构造函数,成员变量都是private static Method+名称形式的,所拥有的方法都是接口中所声明的方法+toString+equals+hashCode三个方法。
接下来通过代码实现用目标对象获取动态代理实例:
public interface Advice {
void beforeAdvice();
}
public class AdviceDemo implements Advice {
@Override
public void beforeAdvice() {
System.out.println("前置通知");
}
}<pre name="code" class="java">public class ProxyTest{
public static void main(String[] args) throws Exception {
//创建目标对象
List<String> strringList = new ArrayList<String>();
//获取代理对象
List<String> proxyList = (List<String>) ProxyTest.getProxy(strringList, new AdviceDemo());
proxyList.add("123");//输出前置通知
}
//获取通用的代理实例
public static Object getProxy(final Object target,final Advice advice){
/*
* 创建代理对象,看newProxyInstance的源码就知道,实际上是先调用getProxyClass方法,然后再通过反射获
* 取他的构造函数将InvocationHandler作为参数创建出实例
*/
Object proxyObject = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(advice instanceof AdviceDemo){
advice.beforeAdvice();
}
Object returnVal = method.invoke(target, args);
/**
* 如果有后置通知,在此处增加即可
*/
return returnVal;
}
});
return proxyObject;
}
}
动态代理原理图如下: