序
这是我参与11月更文挑战的第6天,活动详情查看:[2021最后一次更文挑战]
先引入aop依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.5.2</version>
</dependency>
复制代码
关于AOP 建议读者好好学习理解哈,这里不介绍了,就一句话:很重要很重要!!!
创建编写相关代码
自定义注解
/**
* 自定义 log注解
*
* @author Smile
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
* 记录业务名称
*
* @return 业务名称
*/
String value() default "";
}
复制代码
我这边建立的是方法层级的
切面类
@Resource
private SysLogService logService;
private SysLogPO sysLogPO = new SysLogPO();
/**
* 这个是切点的意思,从什么地方切入: 可以是具体的类或者注解 ,也可以是模糊的类路径 * *..*Api.*(..)
*/
@Pointcut("@annotation(com.smile.ssm.aop.annotation.SysLog)")
public void annotationPointcut() {
}
/**
* 前置通知, 在方法执行之前执行
*
* @param joinPoint xx
*/
@Before("annotationPointcut()")
public void beforePointcut(JoinPoint joinPoint) {
System.out.println("beforePointcut");
}
/**
* 环绕通知, 围绕着方法执行
*
* @param point
* @return
* @throws Throwable
*/
@Around("annotationPointcut()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
System.out.println("doAround");
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
//这个是获取调用的方法类型:get post ...
String method1 = request.getMethod();
String url = request.getRequestURL().toString();
sysLogPO.setMethod(url);
sysLogPO.setDeleted(false);
sysLogPO.setCreateTime(LocalDateTime.now());
//获得执行方法的类名
String targetName = point.getTarget().getClass().getName();
//获得执行方法的方法名
String methodName = point.getSignature().getName();
//获取切点方法的所有参数类型
Object[] arguments = point.getArgs();
try {
Class targetClass = Class.forName(targetName);
//获取公共方法,不包括类私有的
Method[] methods = targetClass.getMethods();
String value = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
//对比方法中参数的个数
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
value = method.getAnnotation(SysLog.class).value();
break;
}
}
}
sysLogPO.setBusinessName(value);
} catch (Exception e) {
e.printStackTrace();
}
logService.save(sysLogPO);
log.info("===请求开始, 各个参数, url: " + url + ", method: " + method1 + ", uri:" + request.getRequestURI() + ", params:" + request.getQueryString());
if (arguments != null) {
for (Object argument : arguments) {
log.info("===接口入参:" + JSON.toJSONString(argument));
}
}
return point.proceed();
}
/**
* 返回通知, 在方法返回结果之后执行
*
* @param joinPoint xx
*/
@AfterReturning("annotationPointcut()")
public void doAfterReturning(JoinPoint joinPoint) {
System.out.println("doAfterReturning");
}
/**
* 异常通知, 在方法抛出异常之后
*/
@AfterThrowing("annotationPointcut()")
public void doAfterThrowing(JoinPoint joinPoint) {
System.out.println("doAfterThrowing");
}
/**
* 后置通知, 在方法执行之后执行
*/
@After("annotationPointcut()")
public void doAfter(JoinPoint joinPoint) {
System.out.println("doAfter");
}
复制代码
@Pointcut
1)execution(* *(..))
//表示匹配所有方法
2)execution(public * com.smile.ssm.service.*Api*(..))
//表示匹配com.smile.ssm.UserService中所有的公有方法
3)execution(* com.smile.ssm..*.*(..))
//表示匹配com.savage.server包及其子包下的所有方法
复制代码
/**
* 搭配切点使用前置通知, 在方法执行之前执行
*
* @param joinPoint xx
*/
@Before("annotationPointcut()")
复制代码
/**
* 环绕通知, 围绕着方法执行
*
* @param point
* @return
* @throws Throwable
*/
@Around("annotationPointcut()")
复制代码
/**
* 返回通知, 在方法返回结果之后执行
*
* @param joinPoint xx
*/
复制代码
/**
* 异常通知, 在方法抛出异常之后
*/
复制代码
/**
* 后置通知, 在方法执行之后执行
*/
复制代码
关于几个增强注解的执行循序
正常情况: doAround->beforePointcut->doAfterReturning->doAfter
异常情况: doAround->beforePointcut->doAfterThrowing->doAfter
特殊情况:
after 里面出现错误的话将 AfterThrowing捕获不到 它只对于被切入的方法所以这里需要注意避免出现多条日志的情况哦。
上面切面类测试代码中写了挺多东西的就是为了测试过程以及搭配:
日志可选择:
- @Before + @After
- @Around
上面两种随便搭配就好!!!
Test
效果:调用业务的日志存库了,并且系统能够打印调用日志!!!
扫描二维码关注公众号,回复:
13469984 查看本文章

END
完美完成aop自定义业务注解啦,下一篇:项目引入服务器端logback日志管理。
今天到洗牙,哇好难受呀,一嘴血味>!-!<