在学习Spring的时候,我们知道Spring主要有两大思想,一个是IOC控制反转,另一个就是AOP面向切面编程,AOP的原理就是java的动态代理机制。
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个是Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的。
InvocationHandler(Interface)
大概意思就是说每一个动态代理类都要实现InvocationHandler,当通过Proxy代理对象调用被代理对象的方法的时候,这个方法就会调用InvocationHandler的invoke方法,实现它,利用多态的动态绑定,实现动态代理!这是InvocationHandler唯一的方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
当代理对象调用被代理对象方法时会把三个参数对应进去,下面有示例
Proxy(Class)
接下来我们来看看Proxy这个类:
/**
* {@code Proxy} provides static methods for creating dynamic proxy
* classes and instances, and it is also the superclass of all
* dynamic proxy classes created by those methods.
* 此处略去好多注释...
*/
类的开头注释中写到,可以通过私有的静态方法创建动态代理对象,它也是所有需要创建动态代理对象的超类,简而言之,要实现动态代理,就要继承Proxy这个类。
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* 此处略去好多注释...
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 此处略去好多代码...
return cons.newInstance(new Object[]{h});
}
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上(就是要去实现InvocationHandler的那个实现类)
示例
现在通过一个示例来看下动态代理模式是什么样的
声明一个接口(JDK代理的缺点,必须以实现接口的形式·,否则代理对象强制转换会出错)
public interface Animal {
public String cry(String str);
public void eat(String str);
}
声明接口的实现类
public class Cat implements Animal{
@Override
public String cry(String str) {
System.out.println("猫"+str+"叫");
return str;
}
@Override
public void eat( String str ) {
System.out.println("猫吃"+str);
}
}
下一步要实现动态代理处理器接口了
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AnimalHandller implements InvocationHandler{
private Object obj;
public AnimalHandller(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这里是代理方法开始");
System.out.println("方法"+method);
Object invoke = method.invoke(obj, args);
System.out.println("这里是代理方法结束");
return invoke;
}
}
写个main方法,创建代理对象调用一下
import java.lang.reflect.Proxy;
public class ProxyClient {
public static void main(String[] args) {
Animal cat = new Cat();
AnimalHandller handller = new AnimalHandller(cat);
Animal instance = (Animal)Proxy.newProxyInstance(ProxyClient.class.getClassLoader(), cat.getClass().getInterfaces(), handller);
System.out.println(instance.getClass().getName());
String cry = instance.cry("喵喵喵");
System.out.println(cry);
instance.eat("猫粮");
}
}
控制台打印信息
com.sun.proxy.$Proxy0
这里是代理方法开始
方法public abstract java.lang.String com.sgcc.uap.proxy.Animal.cry(java.lang.String)
猫喵喵喵叫
这里是代理方法结束
喵喵喵
这里是代理方法开始
方法public abstract void com.sgcc.uap.proxy.Animal.eat(java.lang.String)
猫吃猫粮
这里是代理方法结束
由打印信息可见,创建的对象是代理对象,并非Animal对象
原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Animal类型,所以就可以将其转化为Animal类型了
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
看下方法调用
代理对象调用cry方法时,跳转到AnimalHandller动态代理处理器类执行了invoke方法
方法public abstract java.lang.String com.sgcc.uap.proxy.Animal.cry(java.lang.String)
方法public abstract void com.sgcc.uap.proxy.Animal.eat(java.lang.String)
而打印的两个method正好就是我们的Animal接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。这就是我们的java动态代理机制。