java内功修炼基础篇之---动态代理

引言:

欢迎收看 - 会的不用看,不会的看不懂系列之动态代理

在java学习中,动态代理是一个很基础的知识点,但是这个知识点往往很多人没有将其搞明白,导致在学习Java的一些框架(例如spring,hibernate)时,对其原理模模糊糊,半知半解,今天,我们就将针对动态代理来做一个大白话式的讲解。希望能对大家在学习Java的时候,有所帮助。


Chapter 1:什么是代理

在计算机学科中,很多地方都用到了代理,例如网络中的正向代理,反向代理等。

代理是一个很常见的概念,从字面意思来解释就是你想做一件事情,又不想亲自去做,或者不能亲自去做(出于某种特殊原因),但是又想达到做了那件事情的结果。那么这个时候,就需要请一个人,或者机器去帮助你做这件事情,从而达到想要的目的。

最后这个别人帮你做事的过程,就叫做代理。

例如,你平时要上班,但是你这时候碰巧你的车吃了罚单,需要去交管所一趟。但是公司最近项目很紧,没法让你亲自去,这个时候,你请了你的好朋友A去车管所帮你处理罚单,这样的一个过程,就是代理。


代理在Java中,主要分为两种,一种是静态代理,另一个种是动态代理。

两者的区别在于:

静态代理是你做每一件事情,都要请一个专门的代理。

动态代理是你做任何事情,只需要一个代理就够了,这个代理能帮你做任何事情的代理(23333)。

而我们今天要来说的就是动态代理


Chapter 2:动态代理的实现

在java的使用中,很多地方都用到了动态代理。例如我们熟悉的一些框架,Spring框架的两大核心思想----IOC和AOP,AOP的实现就是使用的动态代理。还有Hibernate中的懒加载,看似是查询得到的一个数据对象,其实得到的是一个代理对象,使用到的技术原理也是动态代理。接下来,我们就来说说动态代理如何实现


Java的动态代理,实现的方式主要有两种:JDK的动态代理,Cglib的动态代理

这两者的区别在于

JDK的动态代理需要基于接口去代理,也就是需要代理的对象需要实现任何一个接口,才能对这个对象进行代理

而Cglib则更为强大一点,可以对任何类进行的动态代理


(1)    JDK动态代理的实现

JDK的动态代理,主要基于两个东西

1.InvocationHandler -- 接口

2.Proxy -- 对象

话不多说,我们先PO代码,再基于代码来解析


首先是服务员接口WaiterInterface,有两个方法,关注其中的服务方法service即可



然后是服务员的实现Waitress,对服务员接口进行的实现



接下来就是核心代码--代理对象处理类


最后是得到代理对象,运行其方法


运行结果



在这个案例中,服务员要对顾客进行服务,但是在服务之前,和服务之后需要做一系列的事情。

然而我们的专职服务员并不想做之前和之后的事情,她只想专注于服务客户,那么这些之前和之后的事情谁来做呢,当然可以交给我们的机器人(代理对象)来做,这样既完成了服务的工作,也完成了擦桌子和收拾桌子的行为。

这里的专注很重要!!!强调一下!!~

就像你在做Router层的时候,需要加日志去记录用户的访问,如果你用的SpringMVC框架,那么你是想在每一个controller的方法上加日志记录代码,还是统一做一个代理去记录日志呢?Ok,如果你的方法比较少,可以每一个方法上加。那么问题来了,我如果想要对用户进行权限控制,你依然是在controller每个方法上加代码去做权限控制吗?显然是不可能的。这样的代码,会越来越乱。这就是专注的重要性,专注自己要做的事情。


回到我们的代码中来,首先是一个Waiter(服务员)接口,大家很容易理解

然后是一个Waitress去实现了Waiter接口,变成了真正的服务员。她实现了service方法和come方法。

那么接下来问题来了,怎么去对这个Waitress进行代理呢。

答案是使用Proxy和InvocationHandler


我们先来看看InvocationHandler的源代码


InvocationHandler是一个接口,只有一个invoke方法。

上面的那段注释,大致意思就是说。每一个代理对象都会关联InvocationHandler的实现类。当你调用代理对象的任何方法时,实则调用的是InvocationHandler的invoke方法


invoke方法有三个参数,proxy代表的是代理对象,method代表的是被代理的对象所被调用的方法,例如上面调用的service。args代表被代理对象调用方法时的参数。

当执行method.invoke时,执行的就是service方法,但是我们可以再service执行之前和之后做一些操作


我们再来说说Proxy。他是一个类,主要用来获取代理对象。Proxy有一个静态方法,专门用来获取代理对象,叫做newProxyInstance


其中有三个参数,第一个参数是你要代理接口实现对象的类加载器,第二个参数是代理接口实现对象的所有接口,第三个参数就是InvocationHandler的实现类。

大致流程如下图


1.编写Waiter接口,并用一个类实现它

2.通过Proxy的静态方法newProxyInstance,传入Waiter实现类对象的类加载器(Loader)和Interfaces,以及InvocationHandler的实现类,获取Waiter接口的代理对象

3.Waiter的代理对象waiter,看似是一个Waiter接口,实则是对于代理对象的引用

4.当调用waiter对象的任何方法时,其实调用的是InvocationHandler的invoke()方法


这就是动态代理的整个流程。



(2)    Cglib动态代理的实现

Cglib的动态代理,不同于JDK的动态代理,JDK的动态代理是基于接口的一种代理,也就是说,某各类必须实现某个接口,才能进行代理。

而Cglib是可以对任何类进行动态代理,相比如JDK的动态代理来说,没有那么局限

那么问题来了,Cglib代理的原理是什么呢?

原来,Cglib是通过字节码技术创建被代理对象类的子类,实现动态代理。

想要使用Cglib的动态代理,首先需要加入一些Jar包(JDK动态代理是Java原生的实现方式,所以不用引入jar包)


是的,你没有看错,需要这四个jar包,什么?你说去哪里下载?maven仓库了解一下。

Cglib的动态代理要做的事情和JDK动态代理是一样的,还是先PO代码


首先是Waitress类



然后是代理对象处理类


最后是运行



可以很明显地看出,cglib获取代理对象的方式是使用Enhancer对象。通过enhancer对象的create()方法,传入被代理对象以及MethodInterceptor的实现类,来获取代理对象。

接着调用代理对象的任何方法,依然调用的是MethodInterceptor的实现类的intercept()方法。

Intercept方法有四个参数,proxy代表被代理对象,method代表被代理对象被执行的方法,args代表被代理对象的方法的参数,methodProxy代表代理对象

当执行method.invoke或者methodProxy.invokeSuper时,实则执行的是实例中的service()方法,我们依然可以在service()方法的执行前后做一些操作,从而达到代理效果。


 CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法(无法继承),无法进行代理。


总结:

以上便是动态代理的全部内容,当然在Java中除了动态代理还有静态代理,但是动态代理的灵活性会高很多,所以多数情况下都是使用动态代理。

动态代理是一个对于初学者来说相对难懂的内容,但是在很多框架中都用到了这个技术,所以还是非常重要的。

感谢您阅读-----会的不用看,不会的看不懂系列

谢谢。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值