spring实战(3)--面向切面编程
发布日期:2021-10-04 02:53:43 浏览次数:7 分类:技术文章

本文共 5930 字,大约阅读时间需要 19 分钟。

一、面向切面编程定义

1、面向切面编程:在软件开发中,散布于应用中多处的功能被称为横切关注点(cross-cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题。

2、描述切面的常用术语有通知(advice)、切点(pointcut)和连接点(join point)。

3、通知(Advice):通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用在某个方法被调用之前?之后?之前和之后都调用?还是只在方法抛出异常时调用?Spring切面可以应用5种类型的通知:

(1)前置通知(Before):在目标方法被调用之前调用通知功能;

(2)后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

(3)返回通知(After-returning):在目标方法成功执行之后调用通知;

(4)异常通知(After-throwing):在目标方法抛出异常后调用通知;

(5)环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

4、连接点(Join point):连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

5、切点(Poincut):如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。

6、切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。

7、引入(Introduction):引入允许我们向现有的类添加新方法或属性。

8、织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:

(1)编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。

(2)类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。

(3)运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。

9、Spring提供了4种类型的AOP支持:前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。如果你的AOP需求超过了简单的方法调用(如构造器或属性拦截),那么你需要考虑使用AspectJ来实现切面。在这种情况下,上文所示的第四种类型能够帮助你将值注入到AspectJ驱动的切面中。

(1)基于代理的经典Spring AOP;

(2)纯POJO切面;

(3)@AspectJ注解驱动的切面;

(4)注入式AspectJ切面(适用于Spring各版本)。

10、Spring通知是Java编写的:Spring所创建的通知都是用标准的Java类编写的。这样的话,我们就可以使用与普通Java开发一样的集成开发环境(IDE)来开发切面。而且,定义通知所应用的切点通常会使用注解或在Spring配置文件里采用XML来编写,这两种语法对于Java开发者来说都是相当熟悉的。

11、Spring在运行时通知对象:通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。

12、Spring只支持方法级别的连接点:通过使用各种AOP方案可以支持多种连接点模型。因为Spring基于动态代理,所以Spring只支持方法连接点。如果需要方法拦截之外的连接点拦截功能,那么我们可以利用Aspect来补充Spring AOP的功能。

二、通过切点来选择连接点

1、切点用于准确定位应该在什么地方应用切面的通知。通知和切点是切面的最基本元素。在Spring AOP中,要使用AspectJ的切点表达式语言来定义切点。Spring仅支持AspectJ切点指示器(pointcutdesignator)的一个子集。在Spring中尝试使用AspectJ其他指示器时,将会抛出IllegalArgument-Exception异常。

2、Spring支持的指示器,注意只有execution指示器是实际执行匹配的,而其他的指示器都是用来限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器。在此基础上,我们使用其他指示器来限制所匹配的切点。

3、编写切点:使用execution()指示器选择Performance的perform()方法。方法表达式以“*”号开始,表明了我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于方法参数列表,我们使用两个点号(…)表明切点要选择任意的perform()方法,无论该方法的入参是什么。可以使用within()指示器来限制匹配,我们使用了“&&”操作符把execution()和within()指示器连接在一起形成与(and)关系(切点必须匹配所有的指示器)。类似地,我们可以使用“||”操作符来标识或(or)关系,而使用“!”操作符来标识非(not)操作。

4、在切点中选择bean:Spring还引入了一个新的bean()指示器,它允许我们在切点表达式中使用bean的ID来标识bean。bean()使用bean ID或bean名称作为参数来限制切点只匹配特定的bean。

5、Spring AOP所支持的AspectJ切点指示器:

arg() 限制连接点匹配参数为指定类型的执行方法

@args() 限制连接点匹配参数由指定注解标注的执行方法

execution() 用于匹配是连接点的执行方法

this() 限制连接点匹配AOP代理的bean引用为指定类型的类

target 限制连接点匹配目标对象为指定类型的类

@target() 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解

within() 限制连接点匹配指定的类型

@within() 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时,方法定义在由指定的注解所标注的类里)

@annotation 限定匹配带有指定注解的连接点

三、使用注解创建切面

1、使用注解创建切面:使用注解来创建切面是AspectJ 5所引入的关键特性。

2、定义切面:Audience类使用@AspectJ注解进行了标注。该注解表明Audience不仅仅是一个POJO,还是一个切面。Audience类中的方法都使用注解来定义切面的具体行为。

3、Spring使用AspectJ注解来声明通知方法:所有的这些注解都给定了一个切点表达式作为它的值

(1)@After 通知方法会在目标方法返回或抛出异常后调用

(2)@AfterReturning 通知方法会在目标方法返回后调用

(3)@AfterThrowing 通知方法会在目标方法抛出异常后调用

(4)@Around 通知方法会将目标方法封装起来

(5)@Before 通知方法会在目标方法调用之前执行

4、@Pointcut注解:能够在一个@AspectJ切面内定义可重用的切点。performance()方法使用了@Pointcut注解。为@Pointcut注解设置的值是一个切点表达式,就像之前在通知注解上所设置的那样。通过在performance()方法上添加@Pointcut注解,我们实际上扩展了切点表达式语言,这样就可以在任何的切点表达式中使用performance()了。performance()方法的实际内容并不重要,在这里它实际上应该是空的。其实该方法本身只是一个标识,供@Pointcut注解依附。

5、启用自动代理:如果你使用JavaConfig的话,可以在配置类的类级别上通过使用EnableAspectJ-AutoProxy注解启用自动代理功能。假如你在Spring中要使用XML来装配bean的话,那么需要使用Spring aop命名空间中的aop:aspectj-autoproxy元素。不管你是使用JavaConfig还是XML,AspectJ自动代理都会为使用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。

6、自动代理注意:Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的指导,切面依然是基于代理的。在本质上,它依然是Spring基于代理的切面。这一点非常重要,因为这意味着尽管使用的是@AspectJ注解,但我们仍然限于代理方法的调用。如果想利用AspectJ的所有能力,我们必须在运行时使用AspectJ并且不依赖Spring来创建基于代理的切面。

7、@Around注解创建环绕通知:@Around注解表明watchPerformance()方法会作为performance()切点的环绕通知。这个新的通知方法,你首先注意到的可能是它接受ProceedingJoinPoint作为参数。这个对象是必须要有的,因为你要在通知中通过它来调用被通知的方法。通知方法中可以做任何的事情,当要将控制权交给被通知的方法时,它需要调用ProceedingJoinPoint的proceed()方法。

8、处理通知中的参数:在切点表达式中声明参数,这个参数传入到通知方法中。切点定义中的参数与切点方法中的参数名称是一样的,这样就完成了从命名切点到通知方法的参数转移。

9、通过注解引入新功能:当引入接口的方法被调用时,代理会把此调用委托给实现了新接口的某个其他对象。实际上,一个bean的实现被拆分到了多个类中。当Spring发现一个bean使用了@Aspect注解时,Spring就会创建一个代理,然后将调用委托给被代理的bean或被引入的实现,这取决于调用的方法属于被代理的bean还是属于被引入的接口。

10、@DeclareParents注解:将Encoreable接口引入到Performance bean中。@DeclareParents注解由三部分组成:

(1)value属性指定了哪种类型的bean要引入该接口。在本例中,也就是所有实现Performance的类型。(标记符后面的加号表示是Performance的所有子类型,而不是Performance本身。)

(2)defaultImpl属性指定了为引入功能提供实现的类。在这里,我们指定的是DefaultEncoreable提供实现。

(3)@DeclareParents注解所标注的静态属性指明了要引入了接口。在这里,我们所引入的是Encoreable接口。

11、面向注解的切面声明有一个明显的劣势:你必须能够为通知类添加注解。为了做到这一点,必须要有源码。如果你没有源码的话,或者不想将AspectJ注解放到你的代码之中,Spring为切面提供了另外一种可选方案。在Spring XML配置文件中声明切面。

四、在XML中声明切面

1、Spring的AOP配置元素能够以非侵入性的方式声明切面,在Spring的aop命名空间中,提供了多个元素用来在XML中声明切面:

aop:advisor 定义AOP通知器

aop:after 定义AOP后置通知(不管被通知的方法是否执行成功)

aop:after-returning 定义AOP返回通知aop:after-throwing 定义AOP异常通知

aop:around 定义AOP环绕通知

aop:aspect 定义一个切面

aop:aspectj-autoproxy 启用 @AspectJ 注解驱动的切面

aop:before 定义一个AOP前置通知

aop:config 顶层的AOP配置元素。大多数的 aop:* 元素必须包含在 aop:config 元素内

aop:declare-parents 以透明的方式为被通知的对象引入额外的接口

aop:pointcut 定义一个切点

2、aop:aspectj-autoproxy元素:它能够自动代理AspectJ注解的通知类。aop命名空间的其他元素能够让我们直接在Spring配置中声明切面,而不需要使用注解。

3、aop:config:关于Spring AOP配置元素,第一个需要注意的事项是大多数的AOP配置元素必须在aop:config元素的上下文内使用。这条规则有几种例外场景,但是把bean声明为一个切面时,我们总是从aop:config元素开始配置的。在aop:config元素内,我们可以声明一个或多个通知器、切面或者切点。

4、在所有的通知元素中,pointcut属性定义了通知所应用的切点,它的值是使用AspectJ切点表达式语法所定义的切点。

5、aop:pointcut:在基于AspectJ注解的通知中,当发现这种类型的重复时,我们使用@Pointcut注解消除了这些重复的内容。而在基于XML的切面声明中,我们需要使用aop:pointcut元素。用pointcut-ref属性来引用这个命名切点。

6、aop:declare-parents声明了此切面所通知的bean要在它的对象层次结构中拥有新的父类型。

五、注入AspectJ切面

1、对于大部分功能来讲,AspectJ切面与Spring是相互独立的。虽然它们可以织入到任意的Java应用中,这也包括了Spring应用,但是在应用AspectJ切面时几乎不会涉及到Spring。

转载地址:https://blog.csdn.net/leif_/article/details/107406139 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:ReactNative(1)--基础入门
下一篇:spring实战(2)--高级装配

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年03月26日 02时00分52秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

python信号采集代码_13行代码实现:Python实时视频采集(附源码) 2019-04-21
h5引入json_纯js直接引入json文件 2019-04-21
python格式化字符串总结_Python字符串处理方法总结 2019-04-21
python中true什么意思_python中的bool是什么意思 2019-04-21
jacobian 矩阵意义_Jacobian矩阵和Hessian矩阵的作用是什么? 2019-04-21
c++ jna 数据类型_JNA 使用总结 2019-04-21
python中如何遍历列表并将列表值赋予_python中如何实现遍历整个列表? 2019-04-21
apache php mysql架构图_Apache+PHP+MYSQL+Tomcat+JK架构设计技巧与应用实战 2019-04-21
mysql 2003错误 10055_MYSQL无法连接---提示10055错误 2019-04-21
mysql redis缓存层_redis实现缓存的两种方式 2019-04-21
mysql索引篇_MySQL索引篇 2019-04-21
有至少一个用MySQL_Mysql有用的面试题 2019-04-21
mysql select同时update_MySQLSELECT同时UPDATE同一张表 2019-04-21
mysql删除后数据库没变化_mysql之delete删除记录后数据库大小不变 2019-04-21
net mysql start3534_MySQL 5.7.14 net start mysql 服务无法启动-“NET HELPMSG 3534” 的奇怪问题... 2019-04-21
pta两个有序链表的合并_7-1 两个有序链表序列的合并 (20分) --- 内存问题再叙 2019-04-21
qpython3安装lxml_在python的lxml中使用xml目录? 2019-04-21
java 幂取模_快速幂取模算法 2019-04-21
java build path jre_java-如何在安装了jre 7后为Jre 6设置路径? 2019-04-21
java上传下载源码_javaweb简单实现文件上传与下载源代码 2019-04-21