自定义注解,利用AOP实现日志保存(数据库),代码全贴,复制就能用
前言
1,在一些特定的场景我们往往需要看一下接口的入参,特别是跨系统的接口调用(下发,推送),这个时候的接口入参就很重要,我们保存入参入库,如果出问题就可以马上定位是上游还是下游的问题(方便扯皮)
2,还有一般需要在系统中看普通日志,还有特殊的异常(报错)日志,一般我们可以通过服务器去查看相应的位置,但是由于服务器是一直运行的,日志是一直在生成的,这个时候就不太方便。
3,保存入参,我们之间本地调试的时候就可以不用造数据,这个也是很方便的,只需改改就好。
这个时候就体现入参请求和相应结果的重要性
Aop基本概念
Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target(目标对象):织入 Advice 的目标对象.。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
自定义注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAccess {
String desc() default "无信息";
}
对自定义注解进行aop切面
一般使用更加详细的日志切面
@Component
@Aspect
public class UserAccessAspect {
// 这里就是对上面进行切面
@Pointcut(value = "@annotation(com.xncoding.aop.aspect.UserAccess)")
public void access() {
}
@Before("access()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
System.out.println("second before");
}
@Around("@annotation(userAccess)")
public Object around(ProceedingJoinPoint pjp, UserAccess userAccess) {
//获取注解里的值
System.out.println("second around:" + userAccess.desc());
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
日志切面
如果想保存到数据库或者进行更加详细的日志打印就可以使用下面的日志切面,
这里目前是打印,如果想保存到数据库,可以创建相应的实体,set进去,然后进行insert到数据库,如果使用mybatis-plus是很方便的,先创建表,然后用mybatis-plus 自动生成代码,再用相应的mapper.inster() 保存到数据库。
/**
* 日志切面
*/
@Aspect
@Component
public class LogAspect {
// 这个目前是对从 com.xncoding.aop.controller.* 下的都进行切入,如果想对上面的自定义注解进行切入,只需改成相对应的路径
// 例如:@Pointcut(value = "@annotation(com.xncoding.aop.aspect.UserAccess)")
@Pointcut("execution(public * com.xncoding.aop.controller.*.*(..))")
public void webLog(){}
@Before("webLog()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("URL : " + request.getRequestURL().toString());
System.out.println("HTTP_METHOD : " + request.getMethod());
System.out.println("IP : " + request.getRemoteAddr());
System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
System.out.println("方法的返回值 : " + ret);
}
//后置异常通知
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("方法异常时执行.....");
}
//后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
@After("webLog()")
public void after(JoinPoint jp){
System.out.println("方法最后执行.....");
}
//环绕通知,环绕增强,相当于MethodInterceptor
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("方法环绕start.....");
try {
Object o = pjp.proceed();
System.out.println("方法环绕proceed,结果是 :" + o);
return o;
} catch (Throwable e) {
throw e;
}
}
}
测试controller
@RestController
@RequestMapping("aop")
public class UserController {
@GetMapping("/first")
public Object first() {
return "first controller";
}
@GetMapping("/doError")
public Object error() {
return 1 / 0;
}
@GetMapping("/second")
@UserAccess(desc = "second")
public Object second() {
return "second controller 666";
}
@PostMapping("/user")
public void userPost(@RequestBody UserVo userVo){
System.out.println("我user进来了");
}
}
user类
@Data
public class UserVo {
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty("性别")
private Integer sex;
}
创建表SQL语句
CREATE TABLE `sys_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`log_type` varchar(50) NOT NULL COMMENT '日志类型',
`create_user_code` varchar(64) NOT NULL COMMENT '创建用户编码',
`create_user_name` varchar(100) NOT NULL COMMENT '创建用户名称',
`create_date` datetime NOT NULL COMMENT '创建时间',
`request_uri` varchar(500) DEFAULT NULL COMMENT '请求URI',
`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
`request_params` text COMMENT '请求参数',
`response_params` text COMMENT '响应参数',
`request_ip` varchar(20) NOT NULL COMMENT '请求IP',
`server_address` varchar(50) NOT NULL COMMENT '请求服务器地址',
`is_exception` char(1) DEFAULT NULL COMMENT '是否异常',
`exception_info` text COMMENT '异常信息',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`execute_time` int(11) DEFAULT NULL COMMENT '执行时间',
`user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',
`device_name` varchar(100) DEFAULT NULL COMMENT '操作系统',
`browser_name` varchar(100) DEFAULT NULL COMMENT '浏览器名称',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_sys_log_lt` (`log_type`) USING BTREE,
KEY `idx_sys_log_cub` (`create_user_code`) USING BTREE,
KEY `idx_sys_log_ie` (`is_exception`) USING BTREE,
KEY `idx_sys_log_cd` (`create_date`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1560559854462500867 DEFAULT CHARSET=utf8 COMMENT='系统日志表';
测试
请求参数:
debug图片
这里只是做打印,也可以在切面里面保存到数据库。
自定义注解,利用AOP实现日志保存(数据库),代码全贴,复制就能用的更多相关文章
- SpringBoot自定义注解、AOP打印日志
前言 在SpringBoot中使用自定义注解.aop切面打印web请求日志.主要是想把controller的每个request请求日志收集起来,调用接口.执行时间.返回值这几个重要的信息存储到数据库里 ...
- 如何通过自定义注解实现AOP切点定义
面向切面编程(Aspect Oriented Programming, AOP)是面向对象编程(Object Oriented Programming,OOP)的强大补充,通过横切面注入的方式引入其他 ...
- springboot aop 自定义注解方式实现完善日志记录(完整源码)
版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...
- Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId
一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...
- 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
- Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志
其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...
- 手写SpringBoot自动配置及自定义注解搭配Aop,实现升级版@Value()功能
背景 项目中为了统一管理项目的配置,比如接口地址,操作类别等信息,需要一个统一的配置管理中心,类似nacos. 我根据项目的需求写了一套分布式配置中心,测试无误后,改为单体应用并耦合到项目中.项目中使 ...
- Spring AOP基础概念及自定义注解式AOP初体验
对AOP的理解开始是抽象的,看到切点的匹配方式其实与正则表达式性质大致一样就基本了解AOP是基本是个什么作用了.只是整个概念更抽象,需要具化理解.下图列表是AOP相关概念解释,可能也比较抽象^_^ 比 ...
- Spring 自定义注解,配置简单日志注解
java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...
随机推荐
- torch.rand、torch.randn、torch.normal、torch.linespace
torch.rand(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) # ...
- Spring Cloud入门看这一篇就够了
目录 SpringCloud微服务 架构演进 服务调用方式: Euraka服务注册中心 注册中心 服务提供者(服务注册) 服务消费者(服务发现) 服务续约 失效剔除和自我保护 Consul 特性 Co ...
- 在海思芯片上使用GDB远程调试
1 前言 使用海思平台上(编译工具链:arm-himix200-linux)交叉编译 GDB 工具(使用版本8.2,之前用过10.2的版本,在编译 gdbserver 遇到编译出错的问题,因为关联了其 ...
- 在jupyter中配置c++内核
安装 xeus-cling conda install xeus-cling -c conda-forg xeus-cling 是一个用于编译解释于C++的Jupyter内核目前,支持Mac与Linu ...
- JS:typeof
想要弄明白某一个变量中保存的数据到底是什么数据类型,我们可以使用到typeof操作符. typeof操作符:检测变量的数据类型. 看例子! var a = "abc"; var b ...
- Obsidian基础教程
Obsidian基础教程 相关链接 2021年新教程 - Obsidian中文教程 - Obsidian Publish 软通达 基础设置篇 1. 开启实时预览 开启实时预览模式,所见即所得 打开设置 ...
- Kali MSF漏洞利用
一.前言 漏洞利用是获得系统控制权限的重要途经.用户从目标系统中找到容易攻击的漏洞,然后利用该漏洞获取权限,从而实现对目标系统的控制. 二.Metasploitable Metasploitable是 ...
- 机器学习基础:用 Lasso 做特征选择
大家入门机器学习第一个接触的模型应该是简单线性回归,但是在学Lasso时往往一带而过.其实 Lasso 回归也是机器学习模型中的常青树,在工业界应用十分广泛.在很多项目,尤其是特征选择中都会见到他的影 ...
- 绝对路径-相对路径和File类的构造方法
绝对路径和相对路径 绝对路径:是一个完整的路径,以盘符开始(c: d:)c:\a.txt 相对路径:相对指的是相对于当前项目的根目录(可以省略项目的根目录) 注意: 1.路径不区分大小写 2.路径中的 ...
- 可变参数和Collections集合工具类的方法_addAll&shuffle
可变参数 可变参数:是JDK1.5之后出现的新特性 使用前提:当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数 使用格式:定义方法时使用 ~修饰符 返回值类型 方法名(数据类 ...