代理模式是常见的一种设计模式,代理模式提供了另外一种访问目标对象的方式,通过代理对象取访问目标对象,这样的话就可以在访问目标对象的前后做一些自己想要的操作,在不改变原有对象功能能前提下,实现功能的增强或扩展。实现方式有静态代理和动态代理。动态代理和静态代理的区别在于代理类生成的时机,静态代理代理类一般是我们在编码时手动实现的,然后再编译成class,而动态代理则是根据我们提供的信息在程序运行期间生成代理类。今天写的这篇文章就是关于JDK动态代理的一些内容。
JDK中已经实现了动态代理机制,其中和动态代理相关的有一个重要的接口InvocationHandler,一个重要的类Proxy。它们都在java.lang.reflect包下面。
首先看下InvocationHandler接口,接口中定义了一个invoke方法,方法中有三个参数,这三个参数的代表含义分别为代理对象、执行方法(真实对象的某一个方法)、方法参数数组。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
接着看下Proxy类这个类有个很重要的方法newProxyInstance,通过调用该方法JVM会生成一个代理类并实例化一个代理对象返回。该方法同样有三个参数,这个三个参数代表的含义分别是类加载器,要实现的接口列表,InvocationHandler实例。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
知道了以上的内容,我们就可以自己实现动态代理了。
首先我们先定义一个Sort接口,这个接口中有个sort方法,可以对整型数组进行排序。下面代码是Sort接口的定义。
public interface Sort {
void sort(int[] array);
}
接着我们实现这个接口,实现这个接口我们采用快速排序的算法。下面代码是QuickSort的实现。
public class QuickSort implements Sort{
@Override
public void sort(int[] array) {
System.out.println("执行快速排序算法");
if (array == null) {
return ;
}
if (array.length < 2) {
return ;
}
quickSort(array, 0, array.length - 1);
System.out.println("排序后");
printArray(array);
}
private void quickSort(int[] array, int start, int end) {
if (start >= end) {
return ;
}
int low = start;
int height = end;
int key = array[low];
while (low < height) {
while(height > low && array[height] > key) {
height--;
}
array[low] = array[height];
while(low < height && array[low] <= key) {
low++;
}
array[height] = array[low];
}
array[low] = key;
quickSort(array, start, low - 1);
quickSort(array, low + 1, end);
}
/**
* 打印数组内容
* @param array
*/
private void printArray(int[] array) {
for (int i : array) {
System.out.print(i);
System.out.print(" ");
}
System.out.println();
}
}
现在我们采用动态代理的方式去计算每次调用快速排序算法的耗时。实现InvocationHandler接口,并且实现计算耗时的功能。代码如下:
public class MyInvocationHandler implements InvocationHandler{
// 代理目标对象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法执行前,记录开始时间
long start = System.currentTimeMillis();
//执行目标对象的方法
method.invoke(target, args);
//方法执行后,记录结束时间
long end = System.currentTimeMillis();
//打印耗时
System.out.println(String.format("排序耗时: %d ms", end - start));
return null;
}
}
实现测试类
public class Test {
public static void main(String[] args) throws IOException {
//加上这行代码可以将动态生成的代理类保存本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//定义待排序数组
int array[] = {10, 8, 12, 31, 5, 1, 7, 8, 30, 10, 15};
//实例化一个快排算法对象
Sort sort = new QuickSort();
//实例化MyInvocationHandler 对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(sort);
//获取一个代理对象
Sort sortProxy = (Sort)Proxy.newProxyInstance(myInvocationHandler.getClass().getClassLoader(), sort.getClass().getInterfaces(), myInvocationHandler);
// 通过代理对象调用排序算法
sortProxy.sort(array);
}
}
运行结果如下图
这就利用JDK的动态代理机制,实现了动态代理模式。