天行健,君子以自强不息;地势坤,君子以厚德载物。
每个人都有惰性,但不断学习是好好生活的根本,共勉!
文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。
文章目录
- 一、介绍
- 二、如何使用AOP
- 1. 标注类为切面类
- 2. 定义切入点函数列表
- 3. 定义签名函数
- 4. 增强方法
- 5. aop全局注解
- 三、代码实现
- 1. 包结构
- 2. 依赖
- 3. 项目启动类
- 4. 请求类
- 5. 切面类
- 四、执行AOP
- 1. 启动程序
- 2. postman访问
- 3. 控制台输出
SpringBoot搭建参考文章:SpringBoot的搭建(两种方式)
一、介绍
-
AOP
aspect oriented programming
面向切面编程 -
作用
在不修改原有代码的基础上,进行代码功能增强 -
描述
简而言之就是不动原有的代码,使用aop之后可以对原有功能进行扩展
如在原有代码执行前执行后进行一些操作的设定
如在原有代码执行后返回结果触发操作
如在原有代码执行报错触发操作
等等
二、如何使用AOP
1. 标注类为切面类
在需要使用aop的类上使用@Aspect
注解标注,标注后该类会被默认为切面类
2. 定义切入点函数列表
在切面类中使用@Pointcut注解列出需要增强的方法(即原有代码的函数,我们就叫它们目标函数
)
语法:execution(修饰符 返回值 包名.包名.包名.类名.方法名(…))
如
@Pointcu("execution(public void com.u.u.test.aop_test.AopController.test(..))")
也可用*替代进行模糊匹配
如
@Pointcu("execution(public * com..aop_test.*Controller.*(..))")
如
@Pointcu("execution(public * com..*Controller.*(..))")
或
@Pointcu("execution(* com..*Controller.*(..))")
3. 定义签名函数
并在@Pointcut
注解下面定义一个没有内容的切入点签名函数(我们将该函数定义为sig()
)
4. 增强方法
在新的方法中定义目标函数的增强内容,并在新的方法上使用注解来指定触发的时机(目标函数执行前、后、返回结果后等)
5. aop全局注解
常用的全局注解有:
前置通知@Before
在被切入的目标方法执行前执行后置通知@After
在被切入的目标方法执行前执行环绕通知@Around
在被切入的目标方法执行前及执行后分别执行返回通知@AfterReturning
在被切入的目标方法执行并成功返回结果后执行
三、代码实现
1. 包结构
基于springboot项目框架,创建一个请求类,一个aop类
2. 依赖
基于springboot进行aop的实现
需要用到依赖如下:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.3</version></dependency><!--json 工具--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.33</version></dependency><!--项目注解工具--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><!--aop--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.7.3</version></dependency></dependencies>
注意:本篇使用2.7.3版本依赖,最新版本可能会报错,请谨慎使用最新版(我用3.2.3报错了)
3. 项目启动类
Application.java
package com.u.u;import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @ClassDescription:* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/3/18 14:12*/
@Aspect
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
4. 请求类
AopController.java
package com.u.u.test.aop_test;import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;/*** @ClassDescription:* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/3/19 14:00*/
@Slf4j
@CrossOrigin
@RestController
@RequestMapping(value = "/aop", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public class AopController {@PostMapping(value = "/test")public Object test(@RequestHeader("token")String token){System.out.println("aop test");return "aop test";}@RequestMapping("/test2")public Object test2(@RequestHeader("token")String token){System.out.println("aop test2");return "aop test2";}}
5. 切面类
AspectExecution.java
package com.u.u.test.aop_test;import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;/*** @ClassDescription: 切面类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/3/19 12:49*/
@Component //将当前类注入到spring容器中
@Slf4j //日志注解
@Aspect //注明该类是切面类
//@Order(2) //如同时又多个aop类,可在类上添加此注解,括号中的值越小,越先执行,如两个aop类中都用了@Before注解那么值为1的@Before先执行
public class AspectExecution {//注解@Order(3)表示,当有多个切面可以使用时,通过@Order注解指定顺序,数值越小越先执行//切入点:这里定义待增强的方法(即需要做统一处理的方法),可以是具体的方法,也可以是模糊定义的一类方法//包名中间可以省略,用".."表示,类名可以用"*Controller"模糊定义类名,方法名同理//这里execution定义的函数就是接下来需要执行全局操作的函数列表@Pointcut("execution(public Object com.u.u.test.aop_test.AopController.test(..))||execution(public Object com.u.u.test.aop_test.AopController.test2(..))")//将以Controller结尾的类名中的所有方法执行切入操作
// @Pointcut("execution(public Object com..aop_test.*Controller.*(..))")@Pointcut("execution(public * com..*Controller.*(..))")//切入点签名public void sig(){
// System.out.println("PointCut签名---》");}@Around("sig()") //环绕通知,参数必须为ProceedingJoinPointpublic Object doAround(ProceedingJoinPoint pJoinPoint){System.out.println("round begin");Object obj = null;try {obj = pJoinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}System.out.println("round end");return obj;}/*** 前置通知* @param joinPoint j*/@Before("sig()") //前置通知,在被切入方法执行之前执行public void doBefore(JoinPoint joinPoint){log.info("=====Before=====");//接收到请求,记录请求内容ServletRequestAttributes srAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert srAttributes != null;HttpServletRequest hsRequest = srAttributes.getRequest();//获取tokenString token = hsRequest.getHeader("token");System.out.println("token: "+token);Cookie[] cookies = hsRequest.getCookies();System.out.println("cookies: " + Arrays.toString(cookies));//记录请求内容log.info("URL:[{}]", hsRequest.getRequestURL().toString());log.info("HTTP_METHOD:[{}]", hsRequest.getMethod());log.info("IP:[{}]", hsRequest.getRemoteAddr());log.info("CLASS_METHOD:[{}]", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());log.info("CLASS_METHOD:[{}]", joinPoint);
// log.info("ARGS:\r\n{}", JSON.toJSONString(joinPoint.getArgs(), String.valueOf(true)));log.info("ARGS:[{}[", JSON.toJSONString(joinPoint.getArgs(), String.valueOf(true)));
// log.info("ARGS:\r\n{}", Arrays.toString(joinPoint.getArgs()));log.info("ARGS:[{}]", Arrays.toString(joinPoint.getArgs()));}/*** 返回通知* @param returnMsg r*/@AfterReturning(returning = "returnMsg", pointcut = "sig()")public void doAfterReturning(Object returnMsg){log.info("======AfterReturning=====");System.out.println("返回通知:" + returnMsg);}/*** 异常通知* @param joinPoint j* @param exception e*/@AfterThrowing(throwing = "exception", pointcut = "sig()") //在被切入的方法抛出异常后执行public void doThrows(JoinPoint joinPoint, Exception exception){log.info("======AfterThrowing=====");System.out.println("异常通知:方法异常时执行");System.out.println("产生异常的方法:" + joinPoint);System.out.println("异常种类:" + exception);}/*** 后置通知* @param joinPoint j*/@After("sig()") //后置通知,在被切入方法执行之后执行,最后执行,并且一定会执行public void doAfter(JoinPoint joinPoint){log.info("======After=====");//接收到请求,记录请求内容ServletRequestAttributes srAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert srAttributes != null;HttpServletRequest hsRequest = srAttributes.getRequest();//记录请求内容log.info("URL:[{}]", hsRequest.getRequestURL().toString());log.info("HTTP_METHOD:[{}]", hsRequest.getMethod());log.info("IP:[{}]", hsRequest.getRemoteAddr());log.info("CLASS_METHOD:[{}]", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());log.info("ARGS:[{}]", JSON.toJSONString(joinPoint.getArgs(), String.valueOf(true)));}}
四、执行AOP
1. 启动程序
2. postman访问
3. 控制台输出
如图输出顺序依次是:
环绕通知开始
前置通知
被切入方法的输出内容
返回通知
后置通知
环绕通知结束
感谢阅读,祝君暴富!