核心类:InvocationHandler、Proxy、classloader
在学习春天的时候,我们知道春天主要有两大思想,一个是国际奥委会,另一个就是AOP,对于国际奥委会,依赖注入就不用多说了,而对于春的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是Java的的的动态代理机制,所以本篇随笔就是对的Java的的动态机制进行一个回顾。
在java的的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler的(接口),另一个则是代理(类),这一个类和接口是实现我们动态代理所必须用到的首先。我们先来看看的Java API的帮助文档是怎么样对这两个类进行描述的:
为InvocationHandler的:
InvocationHandler的是由代理实例的调用处理程序实现的接口。
每个代理实例都有一个关联的调用处理程序。在代理实例上调用方法时,方法调用将被编码并调度到其调用处理程序的调用方法。
一个每代理动态类都必须要实现的InvocationHandler的这个接口,每个并且代理类的实例都关联到了一个处理程序,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由的。InvocationHandler的这个接口的调用方法来进行调用我们来看看的InvocationHandler这个接口的唯一一个方法 调用 方法:
对象调用(Object proxy,Method method,Object [] args)抛出 Throwable
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
对象调用(Object proxy,Method method,Object [] args)抛出 Throwable proxy: 指代我们所代理的那个真实对象 方法: 指代的是我们所要调用真实对象的某个方法的方法对象 args: 指代的是调用真实对象某个方法时接受的参数
如果不是很明白,等下通过一个实例会对这几个参数进行更深的讲解。
接下来我们来看看代理这个类:
代理了提供用于创建³³动态代理类实例状语从句:的静态方法,它也是由这些方法创建的所有动态代理类的超类。
代理这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是用我们的最多的就是 的的newProxyInstance 这个方法:
public static Object newProxyInstance(ClassLoader loader,Class <?> [] interfaces,InvocationHandler h) 抛出 IllegalArgumentException
代理报道查看的实例类 为指定的接口方法调用指派到指定的调用处理程序。
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
public static Object newProxyInstance(ClassLoader loader,Class <?> [] interfaces,InvocationHandler h)抛出IllegalArgumentException loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 接口: 一个接口对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
好了,在介绍完这两个接口(类)以后,我们来通过一个实例来看看我们的动态代理模式是什么样的:
定义一个人的接口
public interface Person {
void findLove();
void aaa();
}
定义一个实现
public class Zhangsan implements Person{
@Override
public void findLove() {
System.out.println("大家好,我是张三,我找女朋友");
}
@Override
public void aaa() {
System.out.println("aaa-");
}
}
下一步,我们就要定义一个动态代理类了,前面说个,每一个动态代理类都必须要实现的InvocationHandler这个接口,因此我们这个动态代理类也不例外:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Meipo implements InvocationHandler{
private Person target;
public Object getInstance(Person person) {
this.target=person;
System.out.println("被代理之前的对象"+person.getClass());
Class<? extends Person> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("媒婆干活");
method.invoke(this.target, args);
return null;
}
}
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.UUID;
import my.MyMeipo;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
Person person = (Person) new Meipo().getInstance(new Zhangsan());
System.out.println("被代理之后的对象"+person.getClass());
/*person.findLove();*/
person.aaa();
}
}
输出结果
被代理之前的对象类proxyTest.Zhangsan
被代理之后的对象类com.sun.proxy。$ Proxy0
媒婆干活
aaa-
/ d:/工作区/ proxyTest / bin中/ proxyTest /
我们首先来看看$ Proxy0这东西
可能我以为报道查看的这个代理对象会的英文人类型的对象,或者是为InvocationHandler的的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为人物类型的对象?原因就是在的的newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,这里因为接口的的英文人类型,所以可以就其将转化为人物类型了。
同时我们一定要记住,通过Proxy.newProxyInstance创建的代理对象是在JVM运行时动态生成的一个对象,它并不是我们的InvocationHandler的类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,代理为中,最后一个数字表示对象的标号。
动态代理的实现思路
(1)拿到被代理对象的引用,然后获取他的接口
(2)JDK代理 重新生成一个类,同时实现我们给的代理对象所实现的接口
(3)吧被代理对象的引用也拿到了
(4)重新动态生成一个class文件
(5)编译