本文共 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!