面向切面编程是什么?
在某些业务场景中,我们的每个模块需要用到一些相同的或类似的辅助功能,例如安全或事务处理。这些功能都是通用的重用的,要实现这些通用或重用的功能我们通常会采取继承或委托来实现,但是在一个应用中使用相同的基类往往会导致一个脆弱的对象体系,而使用委托可能需要对委托对象进行复杂的调试。
切面提供了取代继承和委托的另一种可选方案,使在许多场景下变得简洁与清晰。在使用面向切面编程时,我们在一个地方定义通用功能,但是可以通过声明的方式定义这个功能在何处应用,无需修改原有手影响的类。
这样做有俩个好处:
- 现在每个关注点都集中在同一个地方,而不是分散到多处代码中去;
- 服务模块只需包含它们主要关注的核心代码,次要关注点的代码被转移到切面中去;
Spring借助AspectJ的切点表达式语言来定义Spring切面
常用的指示器-execution
上述图例中表示使用execution()指示器选择包com.springinaction.springidol里面的Instrument类里的play()方法,前面的”*“表示不关心该方法的返回值,方法括号里的“..”表示可以是任意类型的入参。
AOP的术语
- 通知:通知定义了切面什么时候引用;
- 连接点:连接点是在程序执行过程中能够插入的一个点,切面代码可以利用这些点插入到程序的正常流程中;
- 切点:切点有助于缩小切面所通知的连接点的范围;
- 切面:切面时通知和切点的结合;
- 引入:允许我们向现有的类引入新的方法或属性;
- 织入:把切面应用到目标对象并创建新的代理对象的过程;
AOP的术语-通知
Spring切面提供了5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后通知,此时不关心方法返回的是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常之后掉用;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知方法的调用前和调用后执行自定义的行为;
例:
使用注解进行配置切面-声明前置、后置与环绕通知
其中@Aspect注解表示该类不仅是一个POJO,还是一个切面。
可以看到我们上面的例子相同的切面表达式我们重复了四遍,这样又长又重复的表达式使我们的代码看起来不够简洁,如果可以只定义一次,之后在我们需要的时候再调用它会是个不错的办法。
幸好有一个注解可以解决上面的问题:@Pointcut
我们使用该注解把上面的例子改造一下
在编写完切面之后我们需要启用它,并且申明为bean
@EnableAspectJAutoProxy注解为启用AspecJ代理
如果不希望使用注解的话,我们还可以使用XML来进行配置切面,下面是一个简单的例子:
我们在使用环绕通知时需注意,作为环绕通知的方法,它需要接受一个ProceedingJoinPoint类型的参数,该对象的作用为调用被通知的方法,当要将控制权交给被控制的方法时,需调用ProceedingJoinPoint的proceed()方法,如果没有调用,会导致被调用方法阻塞。
正常的执行结果为:
当我们没有调用ProceedingJoinPoint的proceed()方法时,输出结果为: