首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

深入浅出Spring AOP

前言:

AOP是Spring框架除了IOC之外的另一个核心概念。

AOP:Aspect Oriented Programming,意为面向切面编程。

这是一个新的概念,我们知道Java是面向对象编程(OOP):指将所有的一切都看做对象,通过对象与对象之间相互作用来解决问题的一种编程思想。

AOP是对OOP的一个补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。将不同方法的同一位置抽象成一个切面对象,对该切面对象进行编程就是AOP。

AOP的优点:

1.降低模块之间的耦合度。

2.使系统容易扩展。

3.更好的代码复用。

4.非业务代码更加集中,不分散,便于统一管理。

5.业务代码更简洁纯粹,没有其他代码的影响。

说概念太空泛,不好理解,我们还是通过代码来直观感受什么是AOP。

1.创建一个计算器接口 Cal。

定义四个方法:加,减,乘,除。

2.创建接口实现类CalImpl。

实现四个方法。

3.在测试方法中创建CalImpl对象,调用方法。

以上这段代码很简单,现添加功能:在每一个方法执行的同时,打印日志信息:该方法的参数列表和该方法的计算结果。

这个需求很简单,我们只需要在每一个方法体中,运算执行之前打印参数列表,运算结束之后打印计算结果即可,对代码做出如下修改。

再次运行代码,成功打印日志信息。

功能已经实现了,但是我们会发现这种方式业务代码和打印日志代码的耦合性非常高,不利于代码后期的维护。

如果需求改变,需要对打印的日志内容作出修改,那么我们就必须修改4个方法中的所有相关代码,如果是100个方法呢?每次就需要手动去改100个方法中的代码。

换个角度去分析,会发现4个方法中打印日志信息的代码基本相同,那么有没有可能将这部分代码提取出来进行封装,统一维护呢?同时也可以将日志代码和业务代码完全分离开,解耦和。

按照这思路继续向下走,我们希望做的事情是把这4个方法的相同位置(业务方法执行前后)提取出来,形成一个横切面,并且将这个横切面封装成一个对象,将所有的打印日志代码写到这个对象中,以实现与业务代码的分离。

这就是AOP的思想。

如何实现?

使用动态代理的方式来实现。

我们希望CalImpl只进行业务运算,不进行打印日志的工作,那么就需要有一个对象来替代CalImpl进行打印日志的工作,这就是代理对象。

代理对象首先应该具备CalImpl的所有功能,并在此基础上,扩展出打印日志的功能。

1.删除CalImpl方法中所有的打印日志代码,只保留业务代码。

2.创建MyInvocationHandlerl类,并实现InvocationHandler接口,成为一个动态代理类。

bind方法是MyInvocationHandlerl类提供给外部调用的方法,传入需要被代理的对象,bind方法会返回一个代理对象。

bind方法完成了两项工作:

1.将外部传进来的被代理对象保存到成员变量中,因为业务方法调用时需要用到被代理对象。

2.通过Proxy.newProxyInstance方法创建一个代理对象。

解释一下Proxy.newProxyInstance方法的参数:

(1)我们知道对象是JVM根据运行时类来创建的,此时需要动态创建一个代理对象,可以使用被代理对象的运行时类来创建代理对象:obj.getClass().getClassLoader()获取被代理对象的运行时类。

(2)同时代理对象需要具备被代理对象的所有功能,即需要拥有被代理对象的所有接口,所以传入obj.getClass().getInterfaces()。

(3)this指当前MyInvocationHandler对象。

以上全部是反射(reflect)的知识点。

invoke方法:method是描述被代理对象的所有方法的对象,agrs是描述被代理对象方法的参数列表的对象。

method.invoke(this.obj, args)是通过反射机制来调用被代理对象的方法,即业务方法。

所以在method.invoke(this.obj, args)前后添加打印日志信息,就等同于在被代理对象的业务方法前后添加打印日志信息,并且已经做到了分类,业务方法在被代理对象中,打印日志信息在代理对象中。

测试方法中执行代码。

成功,并且现在已经做到了代码分离,CalImpl类中只有业务代码,打印日志的代码写在MyInvocationHandler类中。

以上就是通过动态代理实现AOP的过程,我们在使用Spring框架的AOP时,并不需要这么复杂,Spring已经对这个过程进行了封装,让开发者可以更加便捷的使用AOP进行开发。

接下来我们就来学习Spring框架的AOP如何使用。

在Spring框架中,我们不需要创建动态代理类,只需要创建一个切面类,Spring底层会自动根据切面类以及目标类生成一个代理对象。

1.创建切面类 LoggerAspect

LoggerAspect类名处添加两个注解:

1.@Aspect:表示该类是切面类。

2.@Component:将该类注入到IOC容器。

分别来说明类中的4个方法注解的含义。

1.@Before:表示before方法执行的时机。

即CalImpl所有方法在执行之前会首先执行LoggerAspect类中的before方法。

after方法同理,

表示CalImpl所有方法执行之后会执行LoggerAspect类中的after方法。

afterReturn方法表示CalImpl所有方法在return之后会执行LoggerAspect类中的afterReturn方法。

afterThrowing方法表示CalImpl所有方法在抛出异常时会执行LoggerAspect类中的afterThrowing方法。

所以我们就可以根据具体需求,选择在before,after,afterReturn,afterThrowing方法中添加相应代码。

2.目标类也需要添加@Component注解。

3.spring.xml中进行配置。

2.添加aop:aspectj-autoproxy注解,Spring容器会结合切面类和目标类自动生成动态代理对象,Spring框架的AOP底层就是通过动态代理的方式完成AOP。

4.测试方法执行如下代码:

从IOC容器中获取代理对象,执行方法。

成功。结合代码,回过头来说几个概念更好理解。

切面:横切关注点被模块化的特殊对象。

CalImpl所有方法中需要加入日志的部分,抽象成一个切面对象LoggerAspect。

通知:切面对象完成的工作。

LoggerAspect对象打印日志的操作。

目标:被通知的对象,即被横切的对象。

CalImpl对象。

代理:切面通知目标混合之后的内容。

连接点:程序要执行的某个特定位置。

切面方法要插入业务代码的具体位置。

切点:AOP通过切点定位到连接点。

源码:

链接:https://pan.baidu.com/s/1dUs9cE

密码:wbh6

专业 热爱 专注

致力于最高效的Java学习

Java大联盟

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180206G0653U00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券