Spring系列之四:面向切面的Spring

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liu425151047/article/details/97815380

一、什么是面向切面编程

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

切面:横切关注点可以被模块化为特殊的类,这些类被称为切面( aspect )。

1、定义AOP术语

通知( Advice )

切面的工作被称为通知。通知描述切面的工作,同时决定切面何时工作【定义了切面工作做什么,什么时候做】。

Spring切面可以应用5种类型的通知:

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

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

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

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

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

连接点( Join point)

我们的应用可能有数以千计的时机应用通知,这些时机被称为连接点。比如调用方法,抛出异常等行为。

切点( Poincut)

如果说通知定义了切面的“什么”和“何时”的话, 那么切点就定义了“何处”。切点的定义会匹配通知所要织入的一个或多个连接点。

切面( Aspect)

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

引入( Introduction)

引入允许我们向现有的类添加新方法或属性。 

织入( Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程。 【切面在指定的连接点被织入到目标对象中】

在目标对象的生命周期里有多个点可以进行织入:编译期、类加载期、运行期。Spring AOP是在运行期织入切面的,在织入切面时, AOP容器会为目标对象动态地创建一个代理对象。

总结

参照下图,通知包含了需要用于多个应用对象的横切行为; 连接点是程序执行过程中能够应用通知的所有点; 切点定义了通知被应用的具体位置(在哪些连接点)。其中关键的概念是切点定义了哪些连接点会得到通知。

 

2、Spring对AOP的支持

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

  • 基于代理的经典Spring AOP;

  • 纯POJO切面;

  • @AspectJ注解驱动的切面;

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

前三种都是Spring AOP实现的变体, Spring AOP构建在动态代理基础之上, 因此, Spring对AOP的支持局限于方法拦截。

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

在Spring AOP中, 要使用AspectJ的切点表达式语言来定义切点。 

1、编写切点

为了阐述Spring中的切面, 我们需要有个主题来定义切面的切点。 为此, 我们定义一个Performance接口:

 

Performance可以代表任何类型的现场表演, 如舞台剧、 电影或音乐会。 假设我们想编写Performance的perform()方法触发的通知。

下面的表达式能够设置当perform()方法执行时触发通知的调用。

 

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

三、使用注解创建切面

1、定义切面

从演出的角度来看, 观众是非常重要的, 但是对演出本身的功能来讲,它并不是核心,这是一个单独的关注点。 因此, 将观众定义为一个切面, 并将其应用到演出上就是较为明智的做法。下面定义Audience类:

 

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

Audience有四个方法, 定义了一个观众在观看演出时可能会做的事情。 在演出之前, 观众要就坐(takeSeats()) 并将手机调至静音状态
(silenceCellPhones()) 。 如果演出很精彩的话, 观众应该会鼓掌喝彩(applause()) 。 不过, 如果演出没有达到观众预期的话, 观
众会要求退款(demandRefund()) 。

AspectJ提供了五个注解定义通知,来表明它们应该在什么时候调用:

  • @After:通知方法会在目标方法返回或抛出异常后调用
  • @AfterReturning: 通知方法会在目标方法返回后调用
  • @AfterThrowing: 通知方法会在目标方法抛出异常后调用
  • @Around: 通知方法会将目标方法封装起来
  • @Before: 通知方法会在目标方法调用之前执行

我们可以看到,这四个方法的切点表达式都是相同的。 我们完全可以只定义这个切点一次, 然后每次需要的时候引用它。

@Pointcut注解能够在一个@AspectJ切面内定义可重用的切点。 

 

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

需要注意的是, 除了注解和没有实际操作的performance()方法, Audience类依然是一个POJO。 我们能够像使用其他的Java类那样调用
它的方法,这与其他的Java类并没有什么区别。Audience只是一个Java类, 只不过它通过注解表明会作为切面使用而已。

除此之外,还需要在JavaConfig配置类的类级别上通过使用EnableAspectJ-AutoProxy注解启用自动代理功能。

 

AspectJ自动代理会为使用@Aspect注解的bean创建一个代理, 这个代理会围绕着所有该切面的切点所匹配的bean。

需要记住的是, Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的指导,切面依然是基于代理的。如果想利用AspectJ的所有能力, 我们必须在运行时使用AspectJ并且不依赖Spring来创建基于代理的切面。

 2、创建环绕通知

环绕通知是最为强大的通知类型。 它能够让你所编写的逻辑将被通知的目标方法完全包装起来。 

我们重写Audience切面,使用一个环绕通知来代替之前多个不同的前置通知和后置通知。

 

在这里, @Around注解表明watchPerformance()方法会作为performance()切点的环绕通知。 

它接受ProceedingJoinPoint作为参数,这个对象是必须要有的,当要将控制权交给被通知的方法时, 它需要调用ProceedingJoinPoint的proceed()方法。如果不调用, 那么你的通知实际上会阻塞对被通知方法的调用。 

展开阅读全文

没有更多推荐了,返回首页