点击关注公众号,利用碎片时间学习

1、背景

最近接手一个任务,需要给当前项目加一个较为复杂的日志。有多复杂呢? 要有日志类型、不同日志类型要有不同的操作和备注等。作为小白的我最开始的做法是在业务层写代码记录日志,好处就是方便,坏处就是这种做法直接侵袭Service层,Service层无法做到职责单一了。

经导师点拨,改用自定义注解+AOP切面异步日志

2、技术方案-自定义注解

注解(Annotation),也叫元数据。

2.1 注解介绍

注解其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。

2.2 元注解

元注解用于修饰其他注解的注解,在JDK5.0中提供了四种元注解:Retention、Target、Documented、Inherited

(1) Retention

用于修饰注解,用于指定修饰的哪个注解的声明周期。@Rentention包含一个RetentionPolicy枚举类型的成员变量,使用@Rentention时必须为该value成员变量指定值

RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释,在.class文件中不会保留注解信息

RetentionPolicy.CLASS:在class文件中有效(即class保留),保留在.class文件中,但是当运行java程序时,他就不会继续加载了,不会保留在内存中,JVM不会保留注解。如果注解没有加Retention元注解,那么相当于默认的注解就是这种状态。

RetentionPolicy.RUNTIME:在运行有效(即运行时保留),当运行Java程序时,JVM会保留注释,加载在内存中,那么程序可以通过反射获取该注释。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {RetentionPolicy value();
}public enum RetentionPolicy {SOURCE,CLASS,RUNTIME
}
(2)Target

用于修饰注解的注解,定义当前注解能够修饰程序中的哪些元素(类、属性、方法,构造器等等)

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {ElementType[] value();
}
(3)Documented

用于指定被该元注解修饰的注解类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的,但是加上这个注解生成的文档中就会带着注解了

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
(4)Inherited

被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

注解的基础知识基本介绍完毕,我们开始写起来吧!!!

2.3 实现自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Log {// 日志类型LogType logType() ;// 操作类型OperateType operate();// 备注String note() default "";
}

3、技术方案-AOP切面

AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展, 往往用于实现 日志处理,权限控制,性能检测,事务控制等。AOP实现的原理就是动态代理。在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理。

3.1 AOP术语解析

  • Joint point:类里面那些可以被增强的方法,这些方法被称之为连接点

  • PointCut:实际被增强的方法,这些方法被称之为连接点

  • Advice:实际增加的逻辑部分称为通知

  • Target:被增强功能的对象(被代理的对象)

  • Aspect:Aspect 声明类似与Java中的类声明,在Aspect中会包含着一些PointCut以及相应的Advice.

  • Weaving:创建代理对象并实现功能增强的声明并运行过程将Aspect和其他对象连接起来,并创建Adviced object的过程

3.2 切入点表达式

切入点表达式:通过一个表达式来确定AOP要增强的是哪个或者哪些方法.

3.2 ADVICE通知类型

  • 前置通知:@Before 执行前置通知,并通过JointPoint获取方法中的参数

@Aspect
@Component
public class DaoAspect {/*前置通知:在执行addUser方法之前,执行前置通知,并通过JointPoint获取addUser()方法中的参数*/@Before("execution(* com.xzit.dao.Impl.UserDaoImpl.addUser(..))")public void methodBefore(JoinPoint joinPoint){System.out.println("methodBefore invoked ... ...");Object[] args = joinPoint.getArgs();System.out.println(Arrays.toString(args));}
}
  • 后置通知@After 切点方法是否出现异常,后置通知都会执行

  • 返回通知@AfterReturning 切点出现异常,返回通知不会执行

  • 异常通知@AfterThrowing 切点方法出现异常就执行,不出现异常就不执行

  • 环绕通知@Around 在切点方法之前和之后执行。是@Before@AfterReturing 相结合

3.3 技术实现

根据任务背景,我选择了返回通知@AfterReturning。使用返回通知一定要注意的是:由于我需要用到返回值,所以会用@AfterReturning注解中的returning属性来获取方法的返回值

returning指定的名称必须和切面方法参数中的名称相同。例如在下面代码中指定的"cId"都是相同的

@AfterReturning(pointcut = "@annotation(com.xxx.xxx.xxx.Log)",returning = "cId")
public void handleRdLogs(JoinPoint joinPoint, int cId)

切面方法参数中的参数类型必须和方法返回类型一致

@AfterReturning(pointcut = "@annotation(com.xxx.xxx.xxx.Log)",returning = "cId")
public void handleRdLogs(JoinPoint joinPoint, int cId) {// 获取方法签名MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();// 获取@Log注解内容if (!methodSignature.getMethod().isAnnotationPresent(Log.class)) {log.error("获取注解@Log失败");throw new Exception("获取注解@Log失败");}Log log = methodSignature.getMethod().getAnnotation(Log.class);// 输出日志的备注System.out.println(log.note())
}

3.4 相关操作

(1) 获取方法签名

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();`

(2)根据方法签名获取自定义注解

Log log = methodSignature.getMethod().getAnnotation(Log.class);

(3)根据自定义注解获取,注解内部的参数

System.out.println(log.note())

4、高级操作

场景:不仅需要获取返回值,还得知道方法参数的值,而且方法参数的值不能作为返回值,这个该怎么办呢?

秀操作开始:

第一步: 在注解上写 "#" + "方法参数的名"

@Log(note = "#note")
public int rdAuditReturn(String note) {System.out.println(note)xxxx.....xxxx.....业务代码.....xxxx.....xxxx....return cId;
}

第二步: 实现切面,只要调用这个方法,就可以得到note的值了

private final ExpressionParser parser = new SpelExpressionParser();
private final EvaluationContext evaluationContext = new StandardEvaluationContext();private void getNote(JoinPoint joinPoint, StringBuilder noteBuilder, String note) throws NoSuchMethodException {if (!StringUtils.isBlank(note)) {Class<?> targetCls = joinPoint.getTarget().getClass();MethodSignature ms = (MethodSignature) joinPoint.getSignature();Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();String[] parameterNames = pnd.getParameterNames(targetMethod);Object[] args = joinPoint.getArgs();for (int i = 0; i < args.length; ++i) {int index = i;Optional.ofNullable(args[i]).ifPresent(param -> {String paramName = parameterNames[index];this.evaluationContext.setVariable(paramName, param);});}Optional.ofNullable(this.parser.parseExpression(note).getValue(this.evaluationContext)).ifPresent(k ->noteBuilder.append((String) k));}
}

感谢阅读,希望对你有所帮助 :) 

来源:juejin.cn/post/7148018142944428063

推荐:最全的java面试题库PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

SpringBoot 实现异步记录复杂日志相关推荐

  1. Springboot使用AOP记录请求日志和返回数据

    首先是日志表结构 DROP TABLE IF EXISTS `protal_logvo`; CREATE TABLE `protal_logvo` (`id` varchar(255) NOT NUL ...

  2. 如何使用SpringBoot AOP 记录操作日志、异常日志?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:咫尺的梦想_w cnblogs.com/wm-dv/ ...

  3. 用aspect在springboot中记录操作日志至数据库的详细过程

    代码来自若依管理系统的后台,我截取的其中用于记录操作日志的部分 1.切面 2.操作日志表 3.spring工具类 4.客户端工具类 异步工厂(产生任务用) 异步任务管理器 5.服务层 6.控制层 1. ...

  4. 记录druid整合springboot+logback配置打印sql日志

    [记录druid整合springboot+logback配置打印sql日志] 整合记录 整合记录 首先看 druid 的LogFilter 为我们准备的四种logger类型 这些logger分别对应打 ...

  5. SpringBoot使用Logbook记录HTTP请求响应日志

    写在前面:2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上,内容详细,图文并茂,有需要学习的朋友可以Star一下! GitHub地址:https://github.com/ ...

  6. SpringBoot使用记录之日志Logback

    一个Java小白的学习之路 个人博客 youngljx.top SpringBoot实现使用日志 日志框架 日志的使用 logger的使用 @slf4j 之lombok注解的使用 applicatio ...

  7. SpringBoot AOP 记录操作日志、异常日志

    使用SpringBoot AOP 记录操作日志.异常日志 我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能.在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因 ...

  8. 【实践】万字干货:如何优雅地记录操作日志?(附代码)

    猜你喜欢 1.如何搭建一套个性化推荐系统? 2.从零开始搭建创业公司后台技术栈 3.某视频APP推荐详解(万字长文) 4.微博推荐算法实践与机器学习平台演进 5.腾讯PCG推荐系统应用实践 6.强化学 ...

  9. 美团的系统是如何记录操作日志?

    来源:美团技术团队 操作日志几乎存在于每个系统中,而这些系统都有记录操作日志的一套 API.操作日志和系统日志不一样,操作日志必须要做到简单易懂.所以如何让操作日志不跟业务逻辑耦合,如何让操作日志的内 ...

最新文章

  1. matlab simulink 四分之一1/4车辆垂向振动模型 轮毂电机
  2. lnmp_auto:自动化安装lnmp环境脚本
  3. MIPS衰落 LoongArch崛起
  4. nginx tcp转发_Nginx学习(九):负载均衡服务
  5. Projection投影
  6. ESXI6.7虚拟机配置LACP静态汇聚端口网络负载均衡分流
  7. MySQL远程访问权限,允许远程连接的开启
  8. linux英文包安装教程视频,Linux源码包安装过程讲解
  9. [转]awsome-java
  10. c2c网上商店管理与推广
  11. 服务器安装找不到lsi驱动,IBM 服务器 SAS Raid LSI Windows2008 硬盘 驱动
  12. ALFA机器视觉深度学习外观检测自学习人工智能软件——ocr字符检测
  13. UESTC 1639 云中谁寄锦书来?雁字回时,月满西楼。 Dijkstra拓展
  14. 用Xlsx xlsx-style 导出excel表格,附带合并单元格,文字居中,文字颜色字体大小等样式 (复制即可实现)
  15. 【计算机网络】ICMP协议
  16. 论文投稿-图片处理技巧
  17. 高博SLAM基础课第五讲——PnP非线性优化
  18. Android Studio项目在安卓手机上模拟
  19. ClassNotFoundException:org.exolab.castor.xml.XMLException
  20. 超越Spark,大数据集群计算的生产实践

热门文章

  1. 来看看---程序媛画的腊梅树!!!
  2. __packed 关键字 的作用
  3. 苹果8.1系统无服务器,苹果正式推出iOS8.1系统更新
  4. python 代码换行与字符串换行
  5. tip-of-tree - top-of-tree - ToT
  6. robotframework API 源码阅读笔记----robot.utils.asserts
  7. 疫情之下,想成为刘畊宏那样的强者,你必须三不乱
  8. java resizable方法,[Java教程]jQuery UI resizable使用注意事项、实时等比例拉伸及你不知道的技巧...
  9. 千锋Java高级教程+分布式+springcloud+微信支付课程
  10. stm32毕设 stm32的智能婴儿车系统(源码+硬件+论文)