spring 动态代理_浅谈动态代理

a3bfe098ab9f22793452f50f8012bf49.png

一:什么是代理

在现实生活中,代理很常见。比如,我们需要去某个地方,需要自己开车过去,但是公交车师傅的出现替代了我们自己开车的这个步骤,我们只需要乘坐公交车就可以到达我们需要到达的目的地。这就是一种代理方式。

用自己的话总结一下:代理就是原本需要自己去完成的工作可以由一个新的替代者来完成,这就是代理的一种表现。

二:java中常见代理归类

在java中常见的代理有如下几种:

1. 静态代理

2. 基于JDK的动态代理

3. 基于CGlib的动态代理

4. Spring的动态代理

5. 其他形式的动态代理

三:静态代理

在学习动态代理之前,我们先来了解一下什么是静态代理吧。

比如,公司老板需要告诉所有员工明天公司福利,所有员工放假一天。如果老板自己和员工一个一个说,那么效率多低啊,这时候,如果把这件事情交给秘书来做,对老板来说,是不是就省心了很多呢。

222878c92f74b3a0177237bd078c543f.png

从图中可以看到,静态的表现形式是一个代理对象只能代理一个被代理对象,不能同时代理多个被代理对象。

在java中,静态代理类和被代理类,必须实现同一个接口。

f69a2ef4688a3e8ab9604f34eaa2c092.png

四:基于JDK的动态代理

在上面,我们了解了一下什么是静态代理,一个代理只能代理一个被代理对象,这样的话,如果我们有很多个接口需要代理,那就得写很多个代理类了,如此繁杂的重复工作,有没有更好的方式可以替代的呢?于是,就有了动态代理。

还是和上面一样,在学习动态代理之前,我们先来了解一下什么是动态代理。

比如,公司老板需要招聘员工了,然后就由人事HR小姐姐来完成这项工作,但是HR小姐姐能力有限并不一定能联系上好的求职者,并且很多公司都有面临这项尴尬的问题。于是,老板把这份工作委托给了猎头,让猎头来帮忙完成这项工作。猎头可以同时为多家公司完成招聘工作,这就是动态代理的一种表现形式了。

8f839871ebf2e83d79e7a95773398df1.png

从上图可以看出,动态代理的表现形式即同一个代理对象可以代理多个被代理对象,不再像之前的静态代理仅约束于一个被代理对象。

在java中,基于JDK的代理类和被代理类,必须实现同一个接口。

b71c06bde9c10048e87fe458194b85c4.png

五:基于CGlib的动态代理

上面,JDK的动态代理已经很好的帮我们完成了代理工作,那么,CGlib动态代理又有什么好用的特征呢?

从上面的两张图,我们可以看出,静态代理和基于JDK的动态代理的被代理类都需要实现接口,那么,如果我的类没有实现接口,但是,我想给它配置代理,那么,应该怎么实现呢?

这时候,就轮到CGlib出场了,CGlib可以代理没有接口的类。首先,我们先在pom文件中引入CGlib的jar包。

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.9</version>
</dependency>

CGlib代理大抵如下图所示:

e73aaeae647d6910f9e16ad6598c2771.png

六:基于Spring的动态代理(Spring+Aspectj)

Spring的动态代理是基于JDK的动态代理和CGlib的动态代理而成的。在Spring的动态代理中,有如下配置:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setProxyTargetClass(true);
proxyFactory.setOptimize(true); 
proxyFactory.setFrozen(true);

setProxyTargetClass: 它表明是否代表目标类,默认值为false,意思是代理接口,设置为true后即代理类;

什么意思呢? 意思就是Spring的动态代理默认是基于JDK的动态代理代理接口,当设置proxyTargetClass为true后,即修改为基于CGlib的动态代理代理类。

setOptimize: 它表明对代理生成优化策略,默认值为false,意思是不启用。当设置为true时,代理对象有实现接口时,则使用JDK代理, 代理对象没有实现接口时,则使用CGlib代理。

setFrozen: 即在代理被配置之后,不允许修改代理的配置。

七:比较JDK动态代理和CGlib动态代理

上面我们了解了JDK动态代理和CGlib动态代理,并且知道了JDK代理只能代理有实现接口的被代理对象,而CGlib可以代理没有实现接口的被代理对象并且还可以代理有接口的被代理对象。这样比较下来,感觉CGlib代理好像百利而无一害,那么,直接用CGlib不就可以了,为啥还要使用JDK的动态代理呢?

前辈们根据实际项目经验得知,CGlib创建代理的速度比较慢,但创建代理之后运行的速度却非常快,而JDK动态代理刚好相反。如果在运行的时候不断地用CGlib去创建代理,系统的性能会大打折扣,所以建议一般在系统初始化的时候用CGlib去创建代理,并放到Spring的ApplicationContext中以备后用。

八:动态代理可以实现什么?

前面,我们讲了这么多关于动态代理的常识,那么,动态代理到底可以为我们做什么呢?难道就是像前面提到的那样,代理一个类去做这个类想做的事情吗?如果这样的话,那也太平淡了。

在Spring中,Spring的AOP就是基于动态代理实现的,我们都知道,Spring的核心是AOP和IOC,AOP的核心即切面。在编写项目时,控制台所观察到的日志信息,其实全部都是基于AOP实现的,而AOP的核心就是动态代理。

比如,有这样一个需求:拦截所有的service层实现类,记录每一个实现类的执行时间。按照传统的做法我们会怎么做?在每一个方法的开始添加System.currentTimeMillis()方法获取时间,并且在方法的结束添加一个System.currentTimeMillis()方法获取时间,然后将两个时间相减,来获得时间差吗?如果这样做,想想就知道这个工作量该有多么大啊。

这时候就可以使用动态代理来实现这个需求了。使用动态代理代理所有的类,然后在代理类执行方法前添加时间记录和执行方法后添加时间记录,相减来获取时间差。这样工作量是不是就少了很多呢。其实这种工作,Spring的AOP早就已经帮我们实现好了。

代理可以对方法进行增强,比如原来的被代理对象可以做哪些事情,代理类在完成这个事情的基础上,还可以完成其他的工作,这就是代理的增强。

Spring的AOP中有6种增强方式,依次是:

l Before--------------------------> 前置增强

所谓前置增强,就是在代理的方法执行前完成需要增强的功能

l After-----------------------------> 后置增强

所谓后置增强,就是在代理的方法执行后完成需要增强的功能

l Around ------------------> 环绕增强

所谓环绕增强,就是在代理的方法执行前和执行方法后同时增加需要增强的功能

l AfterReturning ------------> 最终增强

所谓最终增强,可以理解为我们在try{ ...} 异常的时候,使用的finally关键字,也就是一定会,在最后去执行;

l AfterThrowing -------------------------> 抛出异常增强

所谓抛出异常增强,就是在代理的方法在抛出异常时进行功能增强

l DeclareParents ------------> 引入增强

所谓引入增强,引入增强和前面的四种增强方式不同,引入增强是基于类上的,前面四种增强是基于方法的。引入增强可以对类添加一些新的方法和属性。

像上面提到的记录方法执行时间,直接使用环绕增强即可。

使用环绕增强实现记录方法执行时间请参考

似金鱼:浅谈java注解(二)​zhuanlan.zhihu.com
bca3aa1c44e435d89aa3198b0d729e29.png

2018年11月24日 23:08:57

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值