前期回顾:
创建Spring Boot项目
Spring Boot项目基础配置
Spring Boot上传文件
Spring Boot全局异常处理、全局数据、参数预处理
Spring Boot自定义拦截器
Spring Boot整合Servlet、Filter和Listener
AOP作为Spring的一个重点同时也是一个难点,其实我整的也不是很明白,哈哈哈。
进入正题,AOP官方的解释是:面向切面编程
在AOP中有几个概念需要知道:
- Joinpoint:连接点,类里面被增强的方法称为连接点
- Pointcut: 切入点,对增强的方法进行拦截视为切入点
- Advice:通知,在切入点切入需要干的事情,可以分为前置通知、后置通知、异常通知、最终通知和环绕通知
- Aspect:切面,切入点和通知组合为一个切面
- Target:目标对象,上述点整合为一个目标
下面来举例说明:
程序猿A下班时被同事约饭,到达饭店时想起了家有母老虎必须通知啊,也是拿起手机拿起电话一顿解释。接下来就是愉快的喝酒吃饭时间了,饭吃完的他为了等下到家不跪键盘,于是问候下母老虎是否需要带啥吃的给她,(此处有惊喜),到家后通知朋友安全到家。
将A作为目标对象,和同事约饭作为连接点,那么向家里解释就是第一切入点,为前置通知,而吃完饭后的“问候”视为第二切入点,为后置通知,到家后通知朋友安全到家可以当作为最终通知。
接下来通过具体代码来实现:
- maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 定义AOP目标
import com.lei.tang.demo.service.people.ApeService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class ApeSpect {
@Autowired
private ApeService apeService;
@Pointcut(value = "execution(* com.demo.service.people.ApeServiceImpl.haveMeal(..))")
public void logPointcut() {
}
@Before(value = "logPointcut()")
public void before(JoinPoint joinPoint) {
//获取request
//HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
// .getRequest();
//后去请求ip request.getRemoteAddr()
apeService.call("打电话告知家里今天不回去吃饭了");
}
@After(value = "logPointcut()")
public void after(JoinPoint joinPoint) {
apeService.call("告知饭已吃完");
}
@AfterReturning(value = "logPointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
apeService.call("告知朋友顺利到家");
}
@AfterThrowing(value = "logPointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
apeService.call("通知同事他老婆来医院");
}
}
代码解释:
通过@Aspect注解将ApeSpect定义为一个AOP目标对象,需注意的是记得加上@Component注解将它交由Spring管理哦。@Pointcut定义了com.demo.service.people包下的ApeServiceImpl类中的haveMeal方法为连接点,也就是下面切入点能切入的地方,@Before执行方法前执行,@After方法执行完成执行,@AfterReturning正常返回后执行,@AfterThrowing抛出异常后执行。
- service实现
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service("apeService")
public class ApeServiceImpl implements ApeService {
@Override
public String haveMeal(Boolean status) throws InterruptedException {
log.info("吃饭");
if (!status) {
throw new RuntimeException("同事醉酒进医院");
}
return "吃饭";
}
@Override
public void call(String msg) {
log.info("电话====》{}", msg);
}
}
- 来个Controller测试一波
import com.lei.tang.demo.service.people.ApeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ape")
public class ApeController {
@Autowired
ApeService apeService;
/**
* true代表正常吃饭,false表示有人醉酒
*
* @param status
* @return
* @throws InterruptedException
*/
@GetMapping("/haveMeal")
public String haveMeal(@RequestParam Boolean status) throws InterruptedException {
return apeService.haveMeal(status);
}
}
来个正常的:http://localhost:8080/demo/ape/haveMeal?status=true
打印结果:
那么不正常的呢?前面说的惊喜呢?调用:http://localhost:8080/demo/ape/haveMeal?status=false
打印结果:
有没有发现惊喜呢,因为同事醉酒被我送去了医院,所以我没有回家。也就是说当执行方法的过程中抛出了异常,那么将不会进入@AfterReturning注解的方法,则会进入@AfterThrowing方法。
上面一直没有提到环绕通知,最后就来演示下环绕通知,将上面的AOP类改为下面的代码:
import com.lei.tang.demo.service.people.ApeService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class ApeSpect {
@Autowired
private ApeService apeService;
/**
* 定义切入点
*/
@Pointcut(value = "execution(* com.lei.tang.demo.service.people.ApeServiceImpl.haveMeal(..))")
public void logPointcut() {
}
@AfterThrowing(value = "logPointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
apeService.call("通知同事他老婆来医院");
}
@Around(value = "logPointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
apeService.call("打电话告知家里今天不回去吃饭了");
proceedingJoinPoint.proceed();
apeService.call("告知饭已吃完");
}
}
@Around是@Before与@After的组合,proceedingJoinPoint.proceed()方法是执行真正需要执行的方法。
测试调用与上面一致,可以自己去试试控制台打印的结果哦。
如有不到之处,欢迎各位留言指正,不慎感激。
更多文章:
点击跳转CSDN博客
点击跳转简书博客
微信公众号:代码小搬运