《Spring》实战——3.面向切面&AOP

Day_02

前言

在软件开发中,散布于应用中多处的功能被称为横切关注点(crosscutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。

把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题。DI有助于应用对象之间的解耦,而AOP可以实现横切关注点与它们所影响的对象之间的解耦;

1. AOP?

AOP 即 Aspect Oriented Program 面向切面编程;
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能;

所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务;
所谓的周边功能,比如性能统计,日志,事务管理等等;
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面;

在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP;

在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让关注点代码与业务代码分离!

2. AOP的目的?

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

3. AOP中的几个概念?

(1)切入点(Pointcut)

在哪些类,哪些方法上切入(where)

(2)通知(Advice)

在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能)

(3)切面(Aspect)

切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!

(4)织入(Weaving)

把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)

4. Spring对AOP的支持?

Spring提供了4种类型的AOP支持:

(1)基于代理的经典Spring AOP;
(2)纯POJO切面;
(3)@AspectJ注解驱动的切面;
(4)注入式AspectJ切面(适用于Spring各版本)。

前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。Spring缺少对字段连接点的支持,无法让我们创建细粒度的通知,例如拦截对象字段的修改。但是方法拦截可以满足绝大部分的需求,如果需要方法拦截之外的连接点拦截功能,那么我们可以利用Aspect来补充Spring AOP的功能;

5. 定义切点?

定义一个切点(某个方法)所在类的接口;(也可以直接定义在一个POJO里面,但是不利于扩展,使用接口的话,他的所有实现类都可以作为切面)

使用execution()指示器选择Performance的perform()方法。方法表达式以“*”号开始,表明了我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于方法参数列表,我们使用两个点号(..)表明切点要选择任意的perform()方法,无论该方法的入参是什么;

这里先只给出写法,下面介绍怎么在代码中使用;

切点定义为一个接口的方法,范围比较广,适用于所有实现类的bean;可以限制匹配的包/或者bean;

使用bean ID或bean名称作为参数来限制切点只匹配特定的bean:

6. 基于注解的切面定义

定义一个切面(POJO),在其中定义切点(切点是另一个bean类的接口的某个方法);这样切面就和切点”绑定”了;

一个示例:(Audience切面与Performance切点 绑定)

改进:

performance()方法的实际内容并不重要,在这里它实际上应该是空的。其实该方法本身只是一个标识,供@Pointcut注解依附;

测试:

(1)在@configuration的config.java里面定义切面(POJO)的bean和切点的bean;

  • 注意,如果就此止步的话,即便使用了AspectJ注解,这个POJO它并不会被视为切面,这些注解不会解析,也不会创建将其转换为切面的代理;

(2)因此,还要在@configuration上面加一句@EnableAspectJAutoProxy注解启用自动代理功能,eg:

使用XML来装配bean的话,那么需要使用Springaop命名空间中的<aop:aspectj-autoproxy>元素,eg:

(3)在test类里面getBean后,调用切点bean的方法,则可以看到切面”包裹”了该方法;

使用环绕通知?

前置通知和后置通知有一些限制,具体来说,如果不使用成员变量存储信息的话,在前置通知和后置通知之间共享信息非常麻烦;怎么解决?——把前置/后置方法写在一个方法内就行了——使用环绕通知;

环绕通知是最为强大的通知类型,最为灵活;它能够让你所编写的逻辑将被通知的目标方法完全包装起来。实际上就像在一个通知方法中同时编写前置通知和后置通知,eg:

需要注意的是,别忘记调用proceed()方法。如果不调这个方法的话,那么你的通知实际上会阻塞对被通知方法的调用。有意思的是,你可以不调用proceed()方法,从而阻塞对被通知方法的访问,与之类似,你也可以在通知中对它进行多次调用。要这样做的一个场景就是实现重试逻辑,也就是在被通知方法失败后,进行重复尝试。

在切点”附近”调用切面的时机?

当切面需要访问切点方法的方法参数?

如上面的perform()方法内有传参的时候;切点POJO能否访问到这些参数?

——可以!eg:

在POJO里面定义需要参数的切面方法,注意参数的命名与@注解里面一致;

在Spring中,注解和自动代理提供了一种很便利的方式来创建切面。

它非常简单,并且只涉及到最少的Spring配置。但是,面向注解的切面声明有一个明显的劣势:——你必须能够为通知类添加注解。为了做到这一点,必须要有源码。

如果你没有源码的话,或者不想将AspectJ注解放到你的代码之中,Spring为切面提供了另外一种可选方案——在SpringXML配置文件中声明切面;

7. 基于XML的切面声明

与基于注解的方式类似,一个例子,eg:

也是需要将切面类先注册成一个bean,通过ref来引入;POJO里面的方法也要先定义好,使用"method=..."来选择调用的切面方法;

改进:

XML中使用环绕通知?

尽管前面说过,环绕通知可以使用类似@before@after...来实现相同的功能;

但是——存在一个缺点,即@before的方法与@after的方法之间,不能共享状态;如果使用环绕通知,他们就被写在了一个方法内(环绕在proceed前后),就可以共享状态了;

第一步:与注解类似,先在POJO切面类定义环绕方法;

第二步:在XML中配置;

在XML中让切面调用切点的方法参数?

第一步:先在POJO里面定义需要参数的切面方法;

第二步:在XML中配置(与注解类似)

8. 注入AspactJ切面

当Spring AOP不能满足需求时,我们必须转向更为强大的AspectJ;

例如,当我们需要在创建对象时应用通知,构造器切点就非常方便;不像某些其他面向对象语言中的构造器,Java构造器不同于其他的一般方法,这使得Spring基于代理的AOP无法把通知应用于对象的创建过程;

(使用方法-略)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值