Day_02
前言
在软件开发中,散布于应用中多处的功能被称为横切关注点(crosscutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。
把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题。DI有助于应用对象之间的解耦,而AOP可以实现横切关注点与它们所影响的对象之间的解耦;
1. AOP/strong>
AOP 即 Aspect Oriented Program 面向切面编程;
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能;
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务;
所谓的周边功能,比如性能统计,日志,事务管理等等;
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面;
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP;
在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让关注点代码与业务代码分离!
2. AOP的目的/strong>
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
3. AOP中的几个概念/strong>
(1)切入点(Pointcut)
在哪些类,哪些方法上切入(where)
(2)通知(Advice)
在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能)
(3)切面(Aspect)
切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
(4)织入(Weaving)
把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
4. Spring对AOP的支持/strong>
Spring提供了4种类型的AOP支持:
(1)基于代理的经典Spring AOP;
(2)纯POJO切面;
(3)@AspectJ注解驱动的切面;
(4)注入式AspectJ切面(适用于Spring各版本)。
前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。Spring缺少对字段连接点的支持,无法让我们创建细粒度的通知,例如拦截对象字段的修改。但是方法拦截可以满足绝大部分的需求,如果需要方法拦截之外的连接点拦截功能,那么我们可以利用Aspect来补充Spring AOP的功能;
5. 定义切点/strong>
定义一个切点(某个方法)所在类的接口;(也可以直接定义在一个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命名空间中的
(3)在test类里面getBean后,调用切点bean的方法,则可以看到切面”包裹”了该方法;
使用环绕通知/strong>
前置通知和后置通知有一些限制,具体来说,如果不使用成员变量存储信息的话,在前置通知和后置通知之间共享信息非常麻烦;怎么解决—把前置/后置方法写在一个方法内就行了——使用环绕通知;
环绕通知是最为强大的通知类型,最为灵活;它能够让你所编写的逻辑将被通知的目标方法完全包装起来。实际上就像在一个通知方法中同时编写前置通知和后置通知,eg:
需要注意的是,别忘记调用proceed()方法。如果不调这个方法的话,那么你的通知实际上会阻塞对被通知方法的调用。有意思的是,你可以不调用proceed()方法,从而阻塞对被通知方法的访问,与之类似,你也可以在通知中对它进行多次调用。要这样做的一个场景就是实现重试逻辑,也就是在被通知方法失败后,进行重复尝试。
在切点”附近”调用切面的时机/strong>
当切面需要访问切点方法的方法参数/strong>
如上面的perform()方法内有传参的时候;切点POJO能否访问到这些参数/p>
——可以!eg:
在POJO里面定义需要参数的切面方法,注意参数的命名与@注解里面一致;
在Spring中,注解和自动代理提供了一种很便利的方式来创建切面。
它非常简单,并且只涉及到最少的Spring配置。但是,面向注解的切面声明有一个明显的劣势:——你必须能够为通知类添加注解。为了做到这一点,必须要有源码。
如果你没有源码的话,或者不想将AspectJ注解放到你的代码之中,Spring为切面提供了另外一种可选方案——在SpringXML配置文件中声明切面;
7. 基于XML的切面声明
与基于注解的方式类似,一个例子,eg:
也是需要将切面类先注册成一个bean,通过ref来引入;POJO里面的方法也要先定义好,使用”method=…”来选择调用的切面方法;
改进:
XML中使用环绕通知/strong>
尽管前面说过,环绕通知可以使用类似@before@after…来实现相同的功能;
但是——存在一个缺点,即@before的方法与@after的方法之间,不能共享状态;如果使用环绕通知,他们就被写在了一个方法内(环绕在proceed前后),就可以共享状态了;
第一步:与注解类似,先在POJO切面类定义环绕方法;
第二步:在XML中配置;
在XML中让切面调用切点的方法参数/strong>
第一步:先在POJO里面定义需要参数的切面方法;
第二步:在XML中配置(与注解类似)
8. 注入AspactJ切面
当Spring AOP不能满足需求时,我们必须转向更为强大的AspectJ;
例如,当我们需要在创建对象时应用通知,构造器切点就非常方便;不像某些其他面向对象语言中的构造器,Java构造器不同于其他的一般方法,这使得Spring基于代理的AOP无法把通知应用于对象的创建过程;
(使用方法-略)
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览92146 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!