动态代理是java中一种最常用的机制,spring中的核心思想就是IOC和AOP,IOC是依赖注入用到了工厂及反射,而AOP的实现则是通过动态代理。
代理类在程序运行时创建代理类的方式叫做动态代理。与静态代理自己定义代理类,并且代理类在程序运行之前就编译完成的方式不同,动态代理的代理类并不是由代码所定义的,而是由系统的运行时动态生成的。
动态代理的用处:
既然动态代理是AOP的实现,动态代理的作用就应该同AOP相同,即主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。
在动态代理中,有一个重要的类和一个重要的接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。我们来看看API文档中是如何描述的(jdk1.8中文文档)。
java.lang.reflect
Interface InvocationHandler
InvocationHandler
是由代理实例的调用处理程序实现的接口 。每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的
invoke
方法。
上述中关联的调用处理程序是实现InvocationHandler的类,而方法调用被编码则是将方法调用换砖为了Method对象。
InvocationHandler接口中只有一个方法:
public object invoke(Object obj,Method method,Object[] args)
三个参数的含义:
Object obj:指代真正的代理对象
Method method:被代理的方法。开发文档中描述:
-
-
-
所述
方法
对应于调用代理实例上的接口方法的实例。方法
对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
-
-
即代理类的接口所实现的方法
Object[] args:方法的参数。
- java.lang.reflect.Proxy
public class Proxy extends Object implements Serializable
-
Proxy
提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
-
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象(
InvocationHandler
的实现类)。 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke
方法,传递代理实例,java.lang.reflect.Method
被调用方法的java.lang.reflect.Method
对象以及包含参数的类型Object
Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
Proxy类的主要作用就是用来动态的创建代理类,其包含许多方法,其中最常用的方法为newProxyInstance()方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
三个参数的含义如下:
ClassLoader loader:被代理类的类加载器
Class[] interfaces:被代理类所实现的接口
InvocationHandler h:实现InvocationHandler的类
返回一个代理类的实例,可以将其转换为接口类型,该接口将方法调用分派给指定的调用处理程序。
动态代理的具体实现
动态代理就是在代理类和被代理类之间加入了一个实现了InvocationHandler的类,也叫事务处理器。
其具体实现步骤如下:
1.创建被代理的类和接口
2.创建一个接口实现InvocationHandler的类,且它必须实现invoke()方法
3.通过Proxy的静态方法newProxyInstance来创建代理类的对象
4.通过代理类的对象来调用方法
我们通过具体的代码来看:
创建一个接口
public interface Moveable {
void move();
}
创建一个被代理类
public class Car implements Moveable{
public void move(){
System.out.println("汽车行驶中。。。");
}
}
创建一个接口实现InvocationHandler的类,且它必须实现invoke()方法
public class TimeHandler implements InvocationHandler{
private Object obj;
public TimeHandler(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶");
method.invoke(proxy);
Long endTime = System.currentTimeMillis();
System.out.println("汽车行驶结束,使用时间为"+(startTime - endTime));
return null;
}
}
首先、这里面有一个obj,这个obj是必须的,我们既然要做代理,我们必须知道我们是给谁做代理,这里的obj就是被代理者。
动态代理说到底也是代理,代理模式里就要求有一个被代理者。而invoke方法中的第一个参数Object proxy则是代理对象,如果需要对代理对象进行操作可以通过这个参数来实现。
再写一个测试类,在测试类中通过Proxy的静态方法newProxyInstance来创建代理类的对象,并且通过代理类来调用方法。
public class demo {
public static void main(String[] args) {
Car c = new Car();
InvocationHandler h = new TimeHandler(c);
Class<?> cls = c.getClass();
Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
m.move();
System.out.println(m.getClass().getName());
}
}
输出结果
com.sun.proxy.$Proxy0为什么最后一句的输出是这个对象而不是InvocationHandler或者Moveable的接口实现对象呢,在上面介绍Proxy中的第一句话是:
Proxy
提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
Proxy是由newProxyInstance方法创建的所有动态代理类的父类,所以最后的输出是com.sun.proxy.$Proxy0