前言
学习呢,在我看来有输入就要有输出,所谓的输入就是你看的视频,所谓的输出就是当你学完这个课程,你自己对这个视频的理解以及感受,简单地讲,我自己写博客,是为了记录自己学习的过程,当然,如果能给其他人带来帮助,那就更好不过了,旨在对自己学习的知识进行一个有效的输出,不然,看了白看!可能说有一点夸张,但是一点也不为过,举个我自己的例子,我曾经花费了一个多月的时间每天看一个有关于Java8的视频还有Git操作的视频,但是到现在为止,Java8只能说是简单的理解,因为脱离了实际,并且当时只是跟着写了代码案例,看完之后,自己的项目中也没有用到过,很快就忘记一些细节,甚至是以前很常规的操作。现在我在学习的时候,不再追求视频的速度,打个比方: 每天看一个Java8视频还有Git视频或者Spring源码视频,反正每天至少两个视频,首先呢,自己学着也很累,也很费时间,其次,过了一段时间之后就会发现,我特么上个月学了什么玩意?看到的知识竟然跟新的没什么区别!我总结了一下其本质原因在于缺乏自己的思考,以及输出的过程,现在我已经把步调慢了下来,哪怕是两天看一个我也心甘情愿!因为自己会有所输出,或者说深入思考一个程序或者设计模式(实际上用不了这么长时间),下面回归正题,说一下我对于JDK的动态代理模式是怎么样一个理解.
JDK动态代理模式的本意
首先说一下子我自己的理解: 首先我所在的项目用到了Zookeeper + Dubbo 这个服务注册与发现的中间件,其中Zookeeper是一个注册中心,假定我们是一个RPC(Remote Procedure Call),大概说一下这个RPC风格,在我现在所处的项目中分为三个模块,如下图:
项目的架构如图所示:
- 1.cs_api:存放的是实体类对象,就是我们常说的Entity或者Module类
- 2.cs_consumer:从名字上作为一个消费者,那么它就是真正调用服务的,里面一般存放的是MVC架构中C—Controller层的东西
- M:model一般使我们所说的实体类
- V:view视图层,前端发过来请求,后端返回数据,将数据展示给前端
- C:Controller我认为是请求处理层,前端发出请求,之后又Controller进行接收(中间省略了HandlerMapiing映射的过程),之后再交由DAO层进行数据请求
- 3.cs_provider:一般是我们接口真正实现的地方,结合前面的cs_api与cs_consumer,调用过程如下图:
其中调用关系是:当客户端发出请求后,首先会经过CS_Consumer,Cs_Consumer会去Zookeeper上面寻找时候所调用的服务,也就是我们的CS_Provider是否将服务注册到了Zookeeper上面,如果说CS_Provider将服务注册到了Zookpper上面通过Dubbo,那么这个服务就算完成了,值得重视的一点是RPC风格是跨进程的,也就是说不在一个TOMCAT上面,其实说了这么多就是想说明一个问题:这件事CS_Consumer很想自己去完成,但是他自己完成不了,跨进程了所以他只能通过Zookeeper+Dubbo这种服务注册与发现的机制去完成,其中就用到了代理模式,他自己想做却做不了,只能通过别人去做,就是这样子一个道理
具体的细节性剖析,等我们将JDK的动态代理讲完,再回来仔细剖析下Zookeeper + Dubbo所使用的代理模式
我们以三种方式来阐述代理模式
假设我们阐述下面这个例子:
某男子去相亲,我们在这里就管他叫小王吧,小王年龄大了,整个人日渐消瘦,原因是:想找媳妇了,但是奈何职业的原因,没有时间,那么就需要按照如下去定义,为了通用性,不单单是给小王吧,我们给广大单身男性创造一个所通用的接口
- 1.Person如下图
不使用代理模式
先明白一个道理:谁想要去干嘛? —> 小王想要找寻找真爱,从这句话中我们可以分析出几个元素
- 1.小王
- 2.寻找真爱
所以我们的代码,按照如下规格去定义
Person接口:
/**
* @author Zerox
* @date 2019/6/1 19:23
* 首先这是一个接口,为了干什么呢?给广大单身男性提供方便的
*/
public interface Person {
/**
* 寻找真爱,
*/
public void findTrueLove();
}
Xiaowang类:
/**
* @author Zerox
* @date 2019/6/1 19:34
*/
public class XiaoWang implements Person{
private String name = "小王";
private String sex = "男";
/**
* 小王寻找真爱
*/
public void findTrueLove() {
System.out.println("我的名字是" + this.name + ",性别是:" + this.sex);
System.out.println("我想要做的事情是寻找真爱!" );
System.out.println("我的要求是: 肤白貌美大长腿");
}
}
测试类BlogMains
/**
* @author Zerox
* @date 2019/6/1 19:38
*/
public class BlogMain {
public static void main(String[] args) {
Person xiaowang = new XiaoWang();
System.out.println("当前类为:" + xiaowang.getClass());
System.out.println("--- --- --- --- --- --- ---");
xiaowang.findTrueLove();;
}
}
我们可以看到一个东西:—> 即: 当前类为:class blog.XiaoWang,这里我们先打一个问号
使用代理模式
先明白一个道理:谁想要去干嘛? —> 小王想要找寻找真爱,但是小王没时间,所以他打算让媒婆去帮他寻找真爱,我们都知道媒婆有一个资源库(萌妹子库),他需要拿到小王的信息,然后,根据小王的要求,去一个一个寻找,直到寻找到一个合适的,从上面这段话,我们可以大概分析出以下的信息:
- 1.小王
- 2.媒婆
- 3.媒婆需要拿到小王这个对象
- 4.小王想寻找真爱,然后小王没时间,只能让媒婆去帮他找
与上面进行比较,我们少了哪一些过程呢?
- 1.缺少媒婆对象
- 2.媒婆需要拿到小王所有的资料信息
- 3.这次寻找真爱,是媒婆接收了小王的委托,所以才寻找的,所以究其根本,这次寻找对象的人是媒婆代小王寻找,那么下面我们去实现一下
Person接口:
/**
* @author Zerox
* @date 2019/6/1 19:23
* 首先这是一个接口,为了干什么呢?给广大单身男性提供方便的
*/
public interface Person {
/**
* 寻找真爱,
*/
public void findTrueLove();
}
Xiaowang类:
/**
* @author Zerox
* @date 2019/6/1 19:34
*/
public class XiaoWang implements Person{
private String name = "小王";
private String sex = "男";
/**
* 小王寻找真爱
*/
public void findTrueLove() {
System.out.println("我的名字是" + this.name + ",性别是:" + this.sex);
System.out.println("我想要做的事情是寻找真爱!" );
System.out.println("我的要求是: 肤白貌美大长腿");
}
}
MeiPo类
/**
* @author Zerox
* @date 2019/6/1 20:00
*/
public class Meipo implements InvocationHandler {
/**
* 拿到小王这个对象
*/
private Person xiaoWang;
/**
*
* @param xiaowang 被代理的对象
* @return 代理类
*/
public Object getInstance(Person xiaowang) {
this.xiaoWang = xiaowang;
Class<?> clazz = xiaowang.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
};
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是媒婆.我来给你们找小妹妹,嘿嘿嘿~ ");
xiaoWang.findTrueLove();
System.out.println("筛选中。。。");
System.out.println("匹配成功!");
return null;
}
}
测试类
/**
* @author Zerox
* @date 2019/6/1 19:38
*/
public class BlogMain {
public static void main(String[] args) {
Person person = (Person)new Meipo().getInstance(new XiaoWang());
System.out.println("当前调用类为:" + person.getClass());
person.findTrueLove();
}
}
我们可以看到一个东西:—> 即: 当前类为:class com.sun.proxy.$Proxy0g
下面我们来仔细分析一个这个动态代理模式,所涉及到的东西,其本质在于MeiPo类,首先MeiPo这个类他实现了一个接口InvocationHandler,我们来点进去看一下方法的简介如下图:
是这个样子的,她说的是一个代理对象方法的调用过程和他的返回结果,这个方法被调用是当代理对象调用所关联的方法的时候,在我们这里就是当我们生成如下代码:
Person person = (Person)new Meipo().getInstance(new XiaoWang());
System.out.println("当前调用类为:" + person.getClass());
person.findTrueLove()
-
- 第一行是生成代理对象,就是我们这个InvocationHandler的第一个参数proxy,当我们用proxy去调用findTrueLove的时候,就跟我们上面所描述的是一个样子的,但是我们不禁会问什么时候生成的代理对象?
在我们这里是geiInstance()方法会返回一个代理类,其根本原因是我们调用了这样子的一条语句:Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);,我们跟进去看一下,如下图,这个比较简单,但是我们不知道她什么时候去调用的Invoke方法?反正我是不知道的,下面我们再去找一找,如下图所示代码:
很明显的说明了我们的调用过程: 当新生成的代理对象去调用我们的方法的时候,代理对象会去调用他所重新编码和分配的去调用,在我们这里就是调用了invoke方法,讲到这里这个简单的的动态代理模式就差不多到这里结束了,但是有点意犹未尽的感觉,为什么这么说呢!因为哦,最重要的如何生成代理对象的我们不知道!在下一篇博客中,我们将会继续阐述~如有问题,请联系xshlxx@126.om