SpringBoot 之AOP
aop作用
在开发中我们经常使用oop这种纵向结构来开发,但是却会出现一些横切的功能。譬如,日志记录的功能,我们需要在每个方法执行的详细信息通过日志记录,但是我们为每个方法去写日志,明显不合理。再如异常处理功能,我们需要在每个方法执行抛出的异常都专门处理都不合理。这样就需要AOP面向切面开发来处理横切问题。
aop术语
- 通知(advice):
通知主要是定义切面是什么以及何时使用。
Before:在接合点之前执行通知。
AfterReturning:在接合点执行完成之后执行通知。
AfterThrowing:如果从接合点抛出了任何异常,都执行通知。
After:接合点执行完成之后,无论是否抛出了异常,都执行通知。
Around:在接合点周围执行通知,意思就是可能在接合点之前执行,也可能在接合点之后执行。 - 连接点(join point):
意思就是代码中的点,在这个点上开始玩切面。效果肯定是向应用程序中插入额外的逻辑。 - 切点(point cut):
用来选择需要执行一个或者多个连接点的表达式。 - 切面(aspect):
切面就是切点和通知的结合。
通知注解
注解 | 用途 |
---|---|
@Pointcut | 定义切入点 |
@Before | 目标方法执行之前执行 |
@After | 目标方法执行之后必定执行,无论是否报错 |
@AfterReturning | 目标方法有返回值且正常返回后执行 |
@AfterThrowing | 目标方法抛出异常后执行 |
@Around | 可以获取到目标方法的入参和返回值 |
maven导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义切入点
利用execution表达式来给包、类、方法定义切入点。
// 解释:匹配cn.spy.service.impl.MyServiceImpl类下的所有方法
execution(* cn.spy.service.impl.MyServiceImpl.(..))
// 解释:匹配cn.spy.service.impl.MyServiceImpl类下的public void xx();方法。
execution(public void cn.spy.service.impl.MyServiceImpl.(..))
// 解释:匹配cn.spy.service.impl.MyServiceImpl类下第一个参数为String类型,无返回值的所有公共方法。
execution(public void cn.spy.service.impl.MyServiceImpl.*(String,..))
用法:
@Pointcut("execution(public String com.sunpeiyu.demoitem.controller.RedisController.testRequest())")
切入点对象JoinPoint
//获取目标方法的参数信息
Object[] obj = joinPoint.getArgs();
//AOP代理类的信息
joinPoint.getThis();
//代理的目标对象
joinPoint.getTarget();
//用的最多 通知的签名
Signature signature = joinPoint.getSignature();
//代理的是哪一个方法
System.out.println("代理的是哪一个方法"+signature.getName());
//AOP代理类的名字
System.out.println("AOP代理类的名字"+signature.getDeclaringTypeName());
//AOP代理类的类(class)信息
signature.getDeclaringType();
//获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//请求
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//会话
HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
//响应
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
//获取请求参数
Enumeration<String> enumeration = request.getParameterNames();
Map<String,String> parameterMap = new HashMap<>();
while (enumeration.hasMoreElements()){
String parameter = enumeration.nextElement();
parameterMap.put(parameter,request.getParameter(parameter));
}
String str = JSON.toJSONString(parameterMap);
if(obj.length > 0) {
System.out.println("请求的参数信息为:"+str);
}
例子1,给指定方法返回的字符串编码
@Slf4j
@Aspect
@Component
public class ExcrypResultAspect {
@Pointcut("execution(public String com.sunpeiyu.demoitem.controller.RedisController.testRequest())")
public void cutHttpRequest() {
}
@Around(value = "cutHttpRequest()")
public Object encrypt(ProceedingJoinPoint joinPoint) throws Throwable {
String targetResult = (String) joinPoint.proceed();
log.info("=========================== targetResult = " + targetResult);
byte[] encodeArr = Base64.getEncoder().encode(targetResult.getBytes(StandardCharsets.UTF_8));
String base64Str = new String(encodeArr, StandardCharsets.UTF_8);
log.info("=========================== base64Str = " + base64Str);
return base64Str;
}
}
结果:
例子2,将自定义注释的方法,对其返回的字符串编码
// 自定义注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptResult {
String rule() default "MD5";
}
// 切面实现
@Slf4j
@Aspect
@Component
public class ExcrypResultAspect {
@Pointcut("@annotation(com.sunpeiyu.demoitem.aspect.EncryptResult)")
public void cutHttpRequest() {
}
@Around(value = "cutHttpRequest()")
public Object encrypt(ProceedingJoinPoint joinPoint) throws Throwable {
String targetResult = (String) joinPoint.proceed();
log.info("=========================== targetResult = " + targetResult);
EncryptResult encryptResult = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(EncryptResult.class);
String rule = encryptResult.rule();
if (rule.equals("base64")) {
byte[] encodeArr = Base64.getEncoder().encode(targetResult.getBytes(StandardCharsets.UTF_8));
String base64Str = new String(encodeArr, StandardCharsets.UTF_8);
log.info("=========================== base64Str = " + base64Str);
return base64Str;
}
String md5Digest = MD5Utils.md5Digest(targetResult);
log.info("=========================== md5Digest = " + md5Digest);
return md5Digest;
}
}
// 测试
@EncryptResult
@GetMapping("/testRequest")
public String testRequest() {
return "success";
}
结果:
例子3,HttpServletRequest解密
// 请求体解码注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptResult {
}
// 切面方法
@Around(value = "@annotation(com.sunpeiyu.demoitem.aspect.DecryptResult)")
public Object decrypt() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
byte[] httpBody = HttpUtils.getHttpBody(request);
byte[] decodeArr = Base64.getDecoder().decode(httpBody);
return new String(decodeArr, StandardCharsets.UTF_8);
}
参考
https://blog.csdn.net/weixin_45203607/article/details/120248631