SpringBoot项目中使用AOP

1.概述

将通用的逻辑用AOP技术实现可以极大的简化程序的编写,例如验签、鉴权等。Spring的声明式事务也是通过AOP技术实现的。

具体的代码参照 示例项目 https://github.com/qihaiyan/springcamp/tree/master/spring-aop

Spring的AOP技术主要有4个核心概念:

  1. Pointcut: 切点,用于定义哪个方法会被拦截,例如 execution(* cn.springcamp.springaop.service.*.*(..))

  2. Advice: 拦截到方法后要执行的动作

  3. Aspect: 切面,把Pointcut和Advice组合在一起形成一个切面

  4. Join Point: 在执行时Pointcut的一个实例

  5. Weaver: 实现AOP的框架,例如 AspectJ 或 Spring AOP

2. 切点定义

常用的Pointcut定义有 execution 和 @annotation 两种。execution 定义对方法无侵入,用于实现比较通用的切面。@annotation 可以作为注解加到特定的方法上,例如Spring的Transaction注解。

execution切点定义应该放在一个公共的类中,集中管理切点定义。

示例:

1public class CommonJoinPointConfig { 2 @Pointcut("execution(* cn.springcamp.springaop.service.*.*(..))") 3 public void serviceLayerExecution() {} 4}

这样在具体的Aspect类中可以通过 CommonJoinPointConfig.serviceLayerExecution()来引用切点。

1public class BeforeAspect { 2 @Before("CommonJoinPointConfig.serviceLayerExecution()") 3 public void before(JoinPoint joinPoint) { 4 System.out.println(" -------------> Before Aspect "); 5 System.out.println(" -------------> before execution of " + joinPoint); 6 } 7}

当切点需要改变时,只需修改CommonJoinPointConfig类即可,不用修改每个Aspect类。

3. 常用的切面

  1. Before: 在方法执行之前执行Advice,常用于验签、鉴权等。

  2. After: 在方法执行完成后执行,无论是执行成功还是抛出异常.

  3. AfterReturning: 仅在方法执行成功后执行.

  4. AfterThrowing: 仅在方法执抛出异常后执行.

一个简单的Aspect:

1@Aspect 2@Component 3public class BeforeAspect { 4 @Before("CommonJoinPointConfig.serviceLayerExecution()") 5 public void before(JoinPoint joinPoint) { 6 System.out.println(" -------------> Before Aspect "); 7 System.out.println(" -------------> before execution of " + joinPoint); 8 } 9}

4. 自定义注解

假设我们想收集特定方法的执行时间,一种比较合理的方式是自定义一个注解,然后在需要收集执行时间的方法上加上这个注解。

首先定义一个注解TrackTime:

1@Target({ElementType.METHOD, ElementType.TYPE}) 2@Retention(RetentionPolicy.RUNTIME) 3public @interface TrackTime { 4 String param() default ""; 5}

然后再定义一个Aspect类,用于实现注解的行为:

1@Aspect 2@Component 3public class TrackTimeAspect { 4 @Around("@annotation(trackTime)") 5 public Object around(ProceedingJoinPoint joinPoint, TrackTime trackTime) throws Throwable { 6 Object result = null; 7 long startTime = System.currentTimeMillis(); 8 result = joinPoint.proceed(); 9 long timeTaken = System.currentTimeMillis() - startTime; 10 System.out.println(" -------------> Time Taken by " + joinPoint + " with param[" + trackTime.param() + "] is " + timeTaken); 11 return result; 12 } 13}

在某个方法上使用这个注解,就可以收集这个方法的执行时间:

1@TrackTime(param = "myService") 2public String runFoo() { 3 System.out.println(" -------------> foo"); 4 return "foo"; 5}

注意 @TrackTime(param = "myService") 注解是可以传参的。

为了让注解可以传参数,需要在定义注解时指定一个参数String param() default "默认值"

同时在Aspect类中,around方法上加上相应的参数,@Around注解中也需要用参数的变量名trackTime,而不能用类名TrackTime。

1@Around("@annotation(trackTime)") 2public Object around(ProceedingJoinPoint joinPoint, TrackTime trackTime)

5.总结

在运行示例项目时,控制台会输出以下内容:

1 -------------> Before Aspect 2 -------------> before execution of execution(String cn.springcamp.springaop.service.MyService.runFoo()) 3 -------------> foo 4 -------------> Time Taken by execution(String cn.springcamp.springaop.service.MyService.runFoo()) with param[myService] is 8 5 -------------> After Aspect 6 -------------> after execution of execution(String cn.springcamp.springaop.service.MyService.runFoo()) 7 -------------> AfterReturning Aspect 8 -------------> execution(String cn.springcamp.springaop.service.MyService.runFoo()) returned with value foo

可以看出几种 Aspect 的执行顺序依次为 Before After Around AfterReturning(AfterThrowing)

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

SpringBoot项目中使用AOP - HelloWorld