Java设计模式学习笔记:代理模式(二)

上一篇文章介绍了java代理模式的基本概念,以及通过一个生活中的例子介绍了静态代理的原理,传送门如下:

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();
	}
	
}

运行结果如下:

中介媒婆收到相关需求,开始寻找资源
张三找老婆要求:白富美
相亲完成后,双方来电,开始交往
******************************************************
中介媒婆收到相关需求,开始寻找资源
赵六找老婆要求:肤白貌美大长腿
相亲完成后,双方来电,开始交往

从这里可以看到,我们使用代理类时,只需要传不同的代理对象(张三、赵六)就行,而不需要针对张三、赵六分类新建一个代理类,到这里我们已经初步实现了将静态代理转换为动态代理。

下篇文章将继续讲解动态代理的详细原理,以及实现动态代理的两种方式。

你可能感兴趣的:(Java设计模式学习笔记)