上一篇文章介绍了java代理模式的基本概念,以及通过一个生活中的例子介绍了静态代理的原理,传送门如下:
今天继续研究代理模式中另外一种比较重要的模式,动态代理,还是通过上次静态代理的例子来扩展讲解,看看如何从静态代理转为动态代理。
上篇文章中介绍的静态代理,很明显的一个特点就是,通过硬编码的方式实现的代理,不例如扩展,比如说,我们静态代理举的例子中,张三的爸爸帮张三找对象这个事,其实就是张三他爸代理张三做找对象这个事,如果现在赵六也没时间找对象,需要有人帮忙代理,这个时候就必须再找赵六他爸了,就得再写一个赵六他爸这个代理类。我们的动态代理就是来解决这个问题的,既然大家都想找对象,那我们引入中介媒婆这样一个角色,她就不受限制了,既可以给张三介绍对象,也可以给赵六介绍对象。具体的实现方式如下:
我们先说一下通过JDK来实现动态代理的方式,首先建一个代理类:JDKMeiPo,代码如下:
package com.rq.pattern.proxy.dynamicproxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.rq.pattern.proxy.staticproxy.IPerson;
/**
* Title: JDKMeiPo
* Description: 中介媒婆
* @author RQ
* @date 2020年3月23日
*/
public class JDKMeiPo implements InvocationHandler{
private IPerson target;
//1、拿到目标对象的引用(拿到生成之后的代理的实例)
public IPerson getInstance(IPerson target) {
//2、将拿到的IPerson对象先保存起来
this.target = target;
//3、拿到target的class
Class<?> clazz = target.getClass();
//4、调用java反射包里的一个工具类java.lang.reflect.Proxy,
//由这个类调用newProxyInstance方法生成新的字节码生成的代理类,
//此方法需要传3个参数,第一个参数是目标类的类加载器ClassLoader,
//来申明生成的这个类由哪个类加载器加载,第二个参数是申明生成的这个类要实现哪些接口,这个可以通过目标类来获取,
//第三个参数是InvocationHandler,这个参数其实就是当前类本身,这是他本身的一个规范,
//所以此处直接将this传进去,那么就要求当前类实现InvocationHandler这个接口,并实现接口的invoke方法
//这个invoke方法就是自己去控制逻辑的方法,此方法会被生成的类自动调用,返回生成的类的对象
IPerson person = (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
return person;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1、在这里我们可以拿到在外面调用的方法,需要传递两个参数,第一个是调用方法的所属对象,
//注意此处调用的是我们目标类的方法,而不是生成类的方法,所以传的参数是this.target,第二个是需要传的参数
//返回生成的对象,然后我们还可以自定义在调用目标方法之前和之后要执行的逻辑
before();
Object result = method.invoke(this.target, args);
after();
return result;
}
private void before() {
System.out.println("中介媒婆收到相关需求,开始寻找资源");
}
private void after() {
System.out.println("相亲完成后,双方来电,开始交往");
}
}
详细分析一下JDKMeiPo这个代理类的逻辑:
首选,这个类的作用是代理类,他主要是为了代理目标类的方法的调用,那么我们就得先拿到目标类的引用,即拿到动态生成的代理类的实例。第一步通过调用java反射包里的一个工具类java.lang.reflect.Proxy的newProxyInstance方法,通过字节码的方式在内存中生成一个代理类,此方法需要传3个参数,第一个参数是目标类的类加载器ClassLoader,来申明生成的这个类由哪个类加载器加载,第二个参数是申明生成的这个类要实现哪些接口,这个可以通过目标类来获取,第三个参数是InvocationHandler,这个参数其实就是当前类本身,这是他本身的一个规范,所以此处直接将this传进去,那么就要求当前类实现InvocationHandler这个接口,并实现接口的invoke方法,这个invoke方法就是自己去控制逻辑的方法,此方法会被生成的类自动调用,返回生成的类的对象。由于invoke方法需要传递两个参数,第一个是调用方法的所属对象, 注意此处调用的是我们目标类的方法,而不是生成类的方法,所以传的参数是this.target,第二个是调用方法时需要的参数。在这个类中,我们还可以自定义一些方法,例如在调用目标方法之前和调用目标方法之后要执行的逻辑,在我们举的例子中,媒婆在介绍对象之前,先执行物色相亲对象这个事,在目标对象(张三、赵六)执行完找对象这个方法后,媒婆还可以做其他的事,比如事成了要收尾款。
到这里,我们完成了动态代理生成代理类的工作,接下来就是代理类的使用了,代码如下:
package com.rq.pattern.proxy;
import com.rq.pattern.proxy.dynamicproxy.jdkproxy.JDKMeiPo;
import com.rq.pattern.proxy.dynamicproxy.jdkproxy.ZhangSan;
import com.rq.pattern.proxy.dynamicproxy.jdkproxy.ZhaoLiu;
import com.rq.pattern.proxy.staticproxy.IPerson;
public class JDKProxyTest {
public static void main(String[] args) {
JDKMeiPo meipo = new JDKMeiPo();
//首先生成代理对象张三
IPerson zhangsan = meipo.getInstance(new ZhangSan());
zhangsan.findLove();
//再生成代理对象赵六
IPerson zhaoliu = meipo.getInstance(new ZhaoLiu());
zhaoliu.findLove();
}
}
运行结果如下:
中介媒婆收到相关需求,开始寻找资源
张三找老婆要求:白富美
相亲完成后,双方来电,开始交往
******************************************************
中介媒婆收到相关需求,开始寻找资源
赵六找老婆要求:肤白貌美大长腿
相亲完成后,双方来电,开始交往
从这里可以看到,我们使用代理类时,只需要传不同的代理对象(张三、赵六)就行,而不需要针对张三、赵六分类新建一个代理类,到这里我们已经初步实现了将静态代理转换为动态代理。
下篇文章将继续讲解动态代理的详细原理,以及实现动态代理的两种方式。