SpringBoot学习之整合AOP
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
基于SpringBoot整合Aop记录日志
SpringBoot各版本参考文档
https://docs.spring.io/spring-boot/docs/
查找引入依赖
引入依赖
org.springframework.boot
spring-boot-starter-aop
切面类
package link.lycreate.springbooteasyexceldemo.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @ClassName LogAspect
* @Description TODO 日志切面类$
* @Author charlesYan
* @Date 2020/10/9 12:53
* @Version 1.0
**/
@Aspect // 声明是一个切面组件
@Component // 加入到IOC容器
@Slf4j // 等同于 private final Logger logger = LoggerFactory.getLogger(XXX.class);
public class LogAspect {
/**
* @Author charlesYan
* @Description // 指定切入点表达式,拦截那些方法,即为哪些类生成代理对象
* 第一*表示匹配任何返回值的方法
* 第二*表示匹配controller包下的所有类
* 第三*表示匹配类下的所有方法
* ..表示任何个数参数,和如何类型的参数
**/
//@Pointcut("execution(* link.lycreate.springbooteasyexceldemo.controller.*.*(..))")
@Pointcut("@annotation(link.lycreate.springbooteasyexceldemo.aspect.LogFilter)") //在所有标记指定注解的方法上拦截
public void logPointCut(){}
/**
* @Author charlesYan
* @Description //前置通知:在目标方法执行前调用
**/
@Before("logPointCut()")
public void before(JoinPoint joinPoint){
System.out.println("---------------Before Begin CurrentTime = " + System.currentTimeMillis());
/*获取当前请求的HttpServletRequest*/
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
log.info("URL-->"+request.getRequestURL().toString());
log.info("IP-->"+request.getRemoteAddr());
log.info("HTTP_Method-->"+request.getMethod());
log.info("Request_args-->"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("---------------Before End CurrentTime = " + System.currentTimeMillis());
}
/**
* @Author charlesYan
* @Description //返回通知 在目标方法执行后调用,若目标方法出现异常,则不执行
**/
@AfterReturning(value = "logPointCut()",returning = "obj")
public void afterReturning(JoinPoint joinPoint,Object obj){
System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis());
}
/**
* @Author charlesYan
* @Description //后置通知:无论目标方法在执行过程中是否出现异常都会在它之后调用
**/
@After("logPointCut()")
public void after(JoinPoint joinPoint){
System.out.println("---------------After CurrentTime = " + System.currentTimeMillis());
}
/**
* @Author charlesYan
* @Description //异常通知:目标方法抛出异常时执行
**/
@AfterThrowing(value = "logPointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
System.out.println("---------------AfterThrowing CurrentTime = " + System.currentTimeMillis());
}
/**
* @Author charlesYan
* @Description //环绕通知:是前面四个通知的结合体
* 需要在方法之前执行,可以写在joinPoint.procedd();之前
* 需要在方法之后执行,可以写在joinPoint.procedd();之后
**/
@Around("logPointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取目标方法的名称
String methodName = joinPoint.getSignature().getName();
// 获取方法传入参数
Object[] params = joinPoint.getArgs();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取方法上LogFilter注解
LogFilter logFilter = method.getAnnotation(LogFilter.class);
String value = logFilter.value() ;
log.info("模块描述:"+value);
System.out.println("---------------Around Before CurrentTime = " + System.currentTimeMillis() + " method name:" + methodName + " args:" + params[0]);
// 执行源方法
joinPoint.proceed();
System.out.println("---------------Around After CurrentTime = " + System.currentTimeMillis() + " method name:" + methodName + " args:" + params[0]);
}
}自定义注解
/**
* @ClassName LogFilter
* @Description TODO 自定义日志注解类$
* @Author charlesYan
* @Date 2020/10/11 17:59
* @Version 1.0
**/
@Target(ElementType.METHOD)//Target注解决定LogFilter注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分
@Retention(RetentionPolicy.RUNTIME)//Retention注解括号中的"RetentionPolicy.RUNTIME"意思是让LogFilter这个注解的生命周期一直程序运行时都存在
@Documented
public @interface LogFilter {
String value() default "";
}
请求方法
@LogFilter("保存请求日志")
@RequestMapping(path = "/saveRequestLog",method = RequestMethod.POST)
public String saveRequestLog(@RequestParam("name")String name){
return "请求成功:" + name;
}
测试方式
报错信息
方法参数未声明
报错信息
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut截图信息
错误代码
/**
* @Author charlesYan
* @Description //返回通知 在目标方法执行后调用,若目标方法出现异常,则不执行
**/
@AfterReturning("logPointCut()")
public void afterReturning(JoinPoint joinPoint,Object obj){
System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis());
}
正确代码
/**
* @Author charlesYan
* @Description //返回通知 在目标方法执行后调用,若目标方法出现异常,则不执行
**/
@AfterReturning(value = "logPointCut()", returning = "obj")
public void afterReturning(JoinPoint joinPoint,Object obj){
System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis());
}
原因分析
新增的方法参数未赋值
总结
Aop切面通知执行顺序
例图
通知注解中的value属性补充
自定义注解
//注解实体类
package com.trip.demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface SMSAndMailSender {
/*短信模板String格式化串*/
String value() default "";
String smsContent() default "";
String mailContent() default "";
/*是否激活发送功能*/
boolean isActive() default true;
/*主题*/
String subject() default "";
}
切面类中的切面方法
/**
* 在所有标记了@SMSAndMailSender的方法中切入
* @param joinPoint
* @param obj
*/
@AfterReturning(value="@annotation(com.trip.demo.SMSAndMailSender)", returning="obj")
public void afterReturning(JoinPoint joinPoint,Object obj){
System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis());
}
参考链接
SpringBoot整合Aop
https://www.cnblogs.com/fernfei/p/12092510.html
SpringBoot整合aop
https://www.cnblogs.com/myitnews/p/11848159.html
SpringBoot 通过自定义注解实现AOP切面编程实例
https://www.cnblogs.com/lingyejun/p/9941350.html
SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务
https://my.oschina.net/cicadasmile/blog/3073069
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
https://www.cnblogs.com/ChromeT/p/11823735.html
@interface 注解详解
https://blog.csdn.net/u010882691/article/details/82427520
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:
https://blog.csdn.net/weixin_42586723/article/details/109017827
感谢点赞支持下哈
评论