目录
- Spring AOP有多少个通知以及它们的执行顺序
- Spring AOP有多少个通知
- Spring AOP通知的执行顺序
- SpringAOP简单案例
- AOP的五大通知
- AOP的使用方式
Spring AOP有多少个通知以及它们的执行顺序
Spring AOP有多少个通知
- ①前置通知(Before):在连接点执行前执行该通知
- ②正常返回通知(AfterReturning):在连接点正常执行完后执行该通知,若目标方法执行异常则不会执行该通知
- ③异常通知(AfterThrowing):在连接点执行抛出异常时执行该通知
- ④后置通知(after/finally):在连接点执行完成后(不管成功、失败、异常)都会执行该通知
- ⑤环绕通知(Around):围绕在连接点前后
Spring AOP通知的执行顺序
- ①环绕通知:@Around
- ②前置通知:@Before
- ③执行连接点方法
- ④环绕通知:@Around
- ⑤后置通知:@After
- ⑥正常返回通知:@AfterReturning,如果发生异常那么就是异常通知@AfterThrowing
SpringAOP简单案例
本文是一个老师在学校给学生上课的简单案例,介绍了AOP的五个通知的使用,以及通知的执行顺序。通过自定义注解来充当切入点,获取注解的类型分别对不同的老师做对应的业务处理。
代码中的消息响应体(Result)大家可以自定义类型。
AOP的五大通知
- 前置通知:Before
- 环绕通知:Around
- 后置通知:After
- 后置返回通知:AfterReturning
- 后置异常通知:AfterThrowing
执行顺序如下图所示:
AOP的使用方式
1.创建一个课题实体对象
package com.cloud.industryapi.test; | |
import lombok.Data; | |
/** | |
* 课题实体 | |
* @date/3/25 16:26 | |
*/ | |
public class ArticleEntity { | |
/** | |
* PK | |
*/ | |
private Integer id; | |
/** | |
* 课题 | |
*/ | |
private String title; | |
/** | |
* 内容 | |
*/ | |
private String content; | |
} |
2.定义一个切入点,这里以自定义注解的方式实现
package com.cloud.industryapi.test; | |
import java.lang.annotation.*; | |
/** | |
* 切点标识 | |
* @author | |
* @date/3/25 13:09 | |
*/ | |
public PointcutId { | |
String type() default ""; | |
} |
3.声明要织入的切面
package com.cloud.industryapi.test; | |
import lombok.extern.slfj.Slf4j; | |
import org.aspectj.lang.JoinPoint; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.*; | |
import org.springframework.stereotype.Component; | |
/** | |
* 切面 | |
*/ | |
public class ArticleAspect { | |
/** | |
* 定义切入点 | |
*/ | |
public void pointcut(){ | |
} | |
/** | |
* 环绕通知 | |
* @param joinPoint | |
* @param id | |
* @return | |
* @throws Throwable | |
*/ | |
public Object around(ProceedingJoinPoint joinPoint, PointcutId id) throws Throwable { | |
log.info("-------------around start-------------"); | |
Object[] objects = joinPoint.getArgs(); | |
switch (id.type()){ | |
case "language": | |
ArticleEntity language = (ArticleEntity) objects[]; | |
log.info("-------------语文老师课前备课,课题:{}-------------",language.getTitle()); | |
break; | |
case "mathematics": | |
log.info("-------------数学老师课前备课-------------"); | |
break; | |
default: | |
throw new RuntimeException("类型非法"); | |
} | |
//joinPoint.proceed() | |
Object s = joinPoint.proceed(); | |
log.info("-------------叮铃铃铃铃...放学了-------------"); | |
log.info("-------------around end-------------"); | |
return s; | |
} | |
/** | |
* 前置通知 | |
* @param joinPoint | |
* @param id | |
*/ | |
public void before(JoinPoint joinPoint,PointcutId id){ | |
log.info("-------------before start-------------"); | |
log.info("-------------学生进入教室,准备上课-------------"); | |
log.info("-------------before end-------------"); | |
} | |
/** | |
* 后置通知 | |
* @param joinPoint | |
* @param id | |
*/ | |
public void after(JoinPoint joinPoint,PointcutId id){ | |
log.info("-------------after start-------------"); | |
log.info("-------------学校广播:老师们,同学们,中午好,今天学校食堂免费为你们准备了烧鸡,人手一鸡-------------"); | |
log.info("-------------after end-------------"); | |
} | |
/** | |
* 后置返回通知 | |
* @param joinPoint | |
* @param id | |
*/ | |
public void afterReturn(JoinPoint joinPoint, PointcutId id){ | |
log.info("-------------AfterReturning-------------"); | |
log.info("-------------老师们同学们拿着烧鸡回家了-------------"); | |
log.info("-------------学校关闭了大门-------------"); | |
log.info("-------------AfterReturning-------------"); | |
} | |
/** | |
* 后置异常通知 | |
* @param joinPoint | |
* @param id | |
*/ | |
public void afterThrow(JoinPoint joinPoint,PointcutId id){ | |
log.info("-------------AfterThrowing-------------"); | |
log.info("-------------完蛋,小明同学迷路了。。。-------------"); | |
log.info("-------------AfterThrowing-------------"); | |
} | |
} |
注意:ProceedingJoinPoint的proceed()方法相当于前置通知和后置通知的分水岭。
说明:ProceedingJoinPoint的proceed()方法在执行前用来做一些
- 例如:读取日志 ,然后执行目标方法。ProceedingJoinPoint的proceed()方法执行后 ,用来做一些
- 例如:写入日志\color{#0000FF}{说明:ProceedingJoinPoint的proceed()方法在执行前用来做一些
- 例如:读取日志,然后执行目标方法。ProceedingJoinPoint的proceed()方法执行后,用来做一些
- 例如:写入日志}说明:ProceedingJoinPoint的proceed()方法在执行前用来做一些
- 例如:读取日志,然后执行目标方法。ProceedingJoinPoint的proceed()方法执行后,用来做一些
- 例如:写入日志
4.编写控制器
package com.cloud.industryapi.test; | |
import com.cloud.common.kit.Result; | |
import com.cloud.common.page.FrontPagination; | |
import com.cloud.industry.dto.ExclusiveJumpConfigDto; | |
import com.cloud.industry.facede.ExclusiveJumpConfigFacede; | |
import com.cloud.industry.qo.ExclusiveJumpConfigQo; | |
import lombok.extern.slfj.Slf4j; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.web.bind.annotation.PostMapping; | |
import org.springframework.web.bind.annotation.RequestBody; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import java.util.List; | |
/** | |
* 文章 - 控制器 | |
*/ | |
public class ArticleController { | |
/** | |
* 后置异常通知 | |
*/ | |
public void afterThrow(){ | |
log.info("-------------------进入了目标方法-------------------"); | |
System.out.println(/0); | |
} | |
/** | |
* 语文课 | |
* | |
* @param ae | |
* @return | |
*/ | |
public Result language( ArticleEntity ae) { | |
log.info("-------------目标方法开始执行-------------"); | |
log.info("-------------语文老师进入教室,开始讲课《"+ae.getTitle()+"》-------------"); | |
System.out.printf("%s","深蓝的天空中挂着一轮金黄的圆月,下面是海边的沙地,都种着一望无际的碧绿的西瓜。\n" + | |
"其间有一个十一二岁的少年,项带银圈,手捏一柄钢叉,向一匹猹尽力的刺去。\n" + | |
"那猹却将身一扭,反从他的胯下逃走了。\n"); | |
log.info("-------------目标方法结束执行-------------"); | |
return Result.success("执行结束"); | |
} | |
/** | |
* 数学课 | |
* | |
* @param ae | |
* @return | |
*/ | |
public Result mathematics( ArticleEntity ae) { | |
log.info("-------------目标方法开始执行-------------"); | |
log.info("-------------数学老师进入教室-------------"); | |
log.info("-------------“同学们,今天这节数学课,由我代上”-------------"); | |
log.info("-------------“起立”-------------"); | |
log.info("-------------”体~育~老~师~好~~“-------------"); | |
log.info("-------------目标方法结束执行-------------"); | |
return Result.success("执行结束"); | |
} | |
} |
5.请求控制器
最后是请求的响应
完成