Spring AOP SpringBoot集成
上一篇文章《Spring AOP 面向切面编程入门》对AOP作了简要的介绍,包含一些专业术语的解释。
本文基于SpringBoot编写了一个简单的Spring AOPDemo。
maven依赖添加如下
<!--引入SpringBoot的Web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
注意:在完成了引入AOP依赖包后,不需要去做其他配置。AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy,不需要在程序主类中增加@EnableAspectJAutoProxy来启用。
web请求入口:对应系统纵向的核心业务模块。
package com.example.demo.aop; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @desc: 核心业务模块 * @author: CSH **/ @RestController @RequestMapping("/aopController") public class AopController { @RequestMapping(value = "/Curry") public void Curry(){ System.out.println("库里上场打球了!!"); } @RequestMapping(value = "/Harden") public void Harden(){ System.out.println("哈登上场打球了!!"); } @RequestMapping(value = "/Antetokounmpo") public void Antetokounmpo(){ System.out.println("字母哥上场打球了!!"); } @RequestMapping(value = "/Jokic") public void Jokic(){ System.out.println("约基奇上场打球了!!"); } @RequestMapping(value = "/Durant/{point}") public void Durant(@PathVariable("point") int point){ System.out.println("杜兰特上场打球了!!"); } }
定义切面类:在类上添加@Aspect 和@Component 注解即可将一个类定义为切面类。
@Aspect 注解 使之成为切面类
@Component 注解 把切面类加入到IOC容器中
package com.example.demo.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @desc: 经纪人切面 * @author: CSH **/ @Aspect @Component public class BrokerAspect { /** * 定义切入点,切入点为com.example.demo.aop.AopController中的所有函数 *通过@Pointcut注解声明频繁使用的切点表达式 */ @Pointcut("execution(public * com.example.demo.aop.AopController.*(..)))") public void BrokerAspect(){ } /** * @description 在连接点执行之前执行的通知 */ @Before("BrokerAspect()") public void doBeforeGame(){ System.out.println("经纪人正在处理球星赛前事务!"); } /** * @description 在连接点执行之后执行的通知(返回通知和异常通知的异常) */ @After("BrokerAspect()") public void doAfterGame(){ System.out.println("经纪人为球星表现疯狂鼓掌!"); } /** * @description 在连接点执行之后执行的通知(返回通知) */ @AfterReturning("BrokerAspect()") public void doAfterReturningGame(){ System.out.println("返回通知:经纪人为球星表现疯狂鼓掌!"); } /** * @description 在连接点执行之后执行的通知(异常通知) */ @AfterThrowing("BrokerAspect()") public void doAfterThrowingGame(){ System.out.println("异常通知:球迷要求退票!"); } }
切点表达式用于描述切点的位置信息,在此简单描述文中切点表达式的含义。
推荐一个切点表达式总结的博客https://www.cnblogs.com/zhangxufeng/p/9160869.html
这里单独讲解一下功能强大的环绕通知,环绕通知可以将你所编写的逻辑将被通知的目标方法完全包装起来。我们可以使用一个环绕通知来代替之前多个不同的前置通知和后置通知。如下所示,前置通知和后置通知位于同一个方法中,不像之前那样分散在不同的通知方法里面。
/** * @description 使用环绕通知 */ @Around("BrokerAspect()") public void doAroundGame(ProceedingJoinPoint pjp) throws Throwable { try{ System.out.println("经纪人正在处理球星赛前事务!"); pjp.proceed(); System.out.println("返回通知:经纪人为球星表现疯狂鼓掌!"); } catch(Throwable e){ System.out.println("异常通知:球迷要求退票!"); } }
环绕通知接受ProceedingJoinPoint作为参数,它来调用被通知的方法。通知方法中可以做任何的事情,当要将控制权交给被通知的方法时,需要调用ProceedingJoinPoint的proceed()方法。当你不调用proceed()方法时,将会阻塞被通知方法的访问。
当通知方法需要传入参数我们又该怎样处理呢?
和之前创建的切面一样,这里的不同点在于切点还声明了要提供给通知方法的参数。
切点表达式args(point)表明传递给GameDataAspect()方法中的int类型参数也会传递到通知中去,参数名point和缺点方法签名中的参数相匹配
package com.example.demo.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @desc:技术统计 * @author: CSH **/ @Aspect @Component public class GameDataAspect { /** * 定义切入点,切入点为com.example.demo.aop.AopController中的所有函数 *通过@Pointcut注解声明频繁使用的切点表达式 */ @Pointcut("execution(public * com.example.demo.aop.AopController.Durant(int)) && args(point))") public void GameDataAspect(int point){ } /** * @description 使用环绕通知 */ @Around("GameDataAspect(point)") public void doAroundGameData(ProceedingJoinPoint pjp,int point) throws Throwable { try{ System.out.println("球星上场前热身!"); pjp.proceed(); System.out.println("球星本场得到" + point + "分" ); } catch(Throwable e){ System.out.println("异常通知:球迷要求退票!"); } } }
本文代码Github链接https://github.com/LemonFive/SpringBootAopDemo