-
2021-12-27 16:25:56
目录
1.aop说明
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是Spring框架中的一个重要内容,它通过对既有程序定义一个切入点,然后在其前后切入不同的执行内容,比如常见的有:打开数据库连接/关闭数据库连接、打开事务/关闭事务、记录日志等。
基于AOP不会破坏原来程序逻辑,因此它可以很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
而当我们需要使用CGLIB来实现AOP的时候,需要配置spring.aop.proxy-target-class=true,不然默认使用的是标准Java的实现。
实现AOP的切面主要有以下几个要素:
使用@Aspect注解将一个java类定义为切面类
使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。
根据需要在切入点不同位置的切入内容
使用@Before在切入点开始处切入内容
使用@After在切入点结尾处切入内容
使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容
使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑2.实际应用
2.1 引入aop包
<!-- 版本不需要重写,与当前springboot项目保持相同 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2.2 新增切面类
package com.hhmt.delivery.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.Arrays; /** * 辉煌明天 * 原文链接:https://blog.csdn.net/weixin_42236404/article/details/89918737 * FileName: LogAop * Author: huachun * email: huachun_w@163.com * Date: 2021/12/27 15:51 * Description: */ @Aspect @Component public class LogAop { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(LogAop.class); @Pointcut("execution(public * com.hhmt.delivery.*..*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) { // 接收到请求,记录请求内容 String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + ":{}"; String methodParams = Arrays.toString(joinPoint.getArgs()); LOGGER.info("params:" + methodName, methodParams); } @AfterReturning(returning = "data", pointcut = "webLog()") public void doAfterReturning(JoinPoint joinPoint, Object data) { String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + ":{}"; LOGGER.info("return:" + methodName, data); } }
2.3 execution用法说明
第一个 * 指任意访问修饰符
第二个 * 指任何返回类型;
第三个 * 指任何包;
第四个 .*Controller 指任意以Controller结尾的类
第唔个 *(..) 指任意方法
2.4 测试
这里不单独写测试类了,直接访问接口查看服务中的日志
2021-12-28 10:59:18,848: INFO http-nio-9527-exec-3 (LogAop.java:35) - params:com.hhmt.delivery.controller.OcpxCustomerController.customerAscribe:[{redis_key=00501-2021122810-d6c7f763836d44409012, event_type=21, event_time=1640660358650}, 00501] 2021-12-28 10:59:18,848: INFO http-nio-9527-exec-3 (LogAop.java:35) - params:com.hhmt.delivery.ocpx.service.OcpxServiceFactory.customerService:[00501] 2021-12-28 10:59:18,849: INFO http-nio-9527-exec-3 (LogAop.java:42) - return:com.hhmt.delivery.ocpx.service.OcpxServiceFactory.customerService:com.hhmt.delivery.ocpx.service.customer.impl.MeiTuanNetWorkCustomerService@793138bd 2021-12-28 10:59:18,849: INFO http-nio-9527-exec-3 (LogAop.java:35) - params:com.hhmt.delivery.ocpx.service.customer.impl.MeiTuanNetWorkCustomerService.customerAscribe:[{redis_key=00501-2021122810-d6c7f763836d44409012, event_type=21, event_time=1640660358650}, 00501]
2.5 出现的问题
这样在本地测试环境完全看不出来什么问题,到那时放到线上环境之后,由于使用了负载均衡,会有健康监测,所以一直出现了健康检查日志,这样是很不优雅的而且暂用了内存,所以需要指定一下具体的包解决这个问题
解决方法:
1.指定具体的包路径
下面指定了打印具体的包下面的方法日志,问题解决
@Pointcut("execution(public * com.hhmt.delivery.controller..*(..))" + "||execution(public * com.hhmt.delivery.ocpx..*(..))" + "||execution(public * com.hhmt.delivery.service..*(..))" + "||execution(public * com.hhmt.delivery.dao..*(..))" + "||execution(public * com.hhmt.delivery.async..*(..))" + "||execution(public * com.hhmt.delivery.task..*(..))") public void webLog() { }
2.排除健康检查类
3.优化
更多相关内容 -
Springboot接口项目如何使用AOP记录日志
2020-08-19 03:22:19主要介绍了Springboot接口项目如何使用AOP记录日志,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
AOP记录日志
2022-03-15 20:36:25使用aop环绕增强记录日志使用aop环绕增强记录日志
定义LogAnnotation注解
代码
//method代表可以放在方法上 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotation { String module() default ""; String operator() default ""; }
记录日志代码
import com.alibaba.fastjson.JSON; import com.xpblog.utils.HttpContextUtils; import com.xpblog.utils.IpUtils; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; @Component @Aspect //切面 定义了通知和切点的关系 @Slf4j public class LogAspect { //需写加强的详细路径 @Pointcut("@annotation(com.xpblog.common.aop.LogAnnotation)") public void pt(){} //环绕通知 @Around("pt()") public Object log(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //执行方法 Object result = point.proceed(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 recordLog(point, time); return result; } private void recordLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); log.info("=====================log start================================"); log.info("module:{}",logAnnotation.module()); log.info("operation:{}",logAnnotation.operator()); //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.info("request method:{}",className + "." + methodName + "()"); // //请求的参数 Object[] args = joinPoint.getArgs(); String params = JSON.toJSONString(args[0]); log.info("params:{}",params); log.info("ip:{}", IpUtils.getIpAddr()); log.info("excute time : {} ms",time); log.info("=====================log end================================"); } }
工具类
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; public class HttpContextUtils { public static HttpServletRequest getHttpServletRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } }
port lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; /** * 获取Ip * */ @Slf4j public class IpUtils { /** * 获取IP地址 * <p> * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 */ public static String getIpAddr(HttpServletRequest request) { String ip = null, unknown = "unknown", seperator = ","; int maxLength = 15; try { ip = request.getHeader("x-forwarded-for"); if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } } catch (Exception e) { log.error("IpUtils ERROR ", e); } // 使用代理,则获取第一个IP地址 if (StringUtils.isEmpty(ip) && ip.length() > maxLength) { int idx = ip.indexOf(seperator); if (idx > 0) { ip = ip.substring(0, idx); } } return ip; } /** * 获取ip地址 * * @return */ public static String getIpAddr() { //获取request 设置IP地址 HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); return getIpAddr(request); } }
-
使用AOP记录日志
2021-02-19 14:21:04这让我想起有以前我还是一个菜鸟时接到的一个需求,要在所有接口中记录请求日志,因为项目比较老了,所以接口还是挺多的,我预估需要三天才能完成,我们老大鄙视了我一眼,然后花半小时就完成了。 今天我们就来讲讲...前面我们已经介绍了AOP适用的一些业务场景以及简单的代码实现,当时我们的切点是用execution表达式来配置的,这种方式有一些局限性在里面:
- 灵活性不高,一个表达式只能切到某种同类型的方法
- 个性化不足,很难对切面切到的所有方法中的一些做一些个性化的设置
这让我想起有以前我还是一个菜鸟时接到的一个需求,要在所有接口中记录请求日志,因为项目比较老了,所以接口还是挺多的,我预估需要三天才能完成,我们老大鄙视了我一眼,然后花半小时就完成了。
今天我们就来讲讲切点的另一种配置方式:@annotation,通过@annotation配置切点,我们可以灵活的控制切到哪个方法,同时可以进行一些个性化的设置,今天我们就用它来实现一个记录所有接口请求功能吧。
首先来看看我们要实现的效果
通过结果我们可以看到,我们的自定义注解EagleEye做到了统一的记录下了请求链接、请求类型、请求IP、请求入参、请求耗时、请求返回等信息。
是不是感觉还不错呢?下面我们就来一起动手实现它吧!
添加依赖
新建一个Spring Boot项目,打开pom.xml文件添加相关Maven依赖:
自定义一个注解
我们定义了一个注解,定义注解使用@interface:- 定义了注解的生命周期为运行时
- 定义了注解的作用域为方法
- 标识该注解可以被JavaDoc记录
- 定义注解名称为EagleEye(鹰眼,哈哈~~)
- 定义一个元素desc,用来描述被修饰的方法
注解虽然定义好了,但是还用不了,因为没有具体的实现逻辑,接下来我们用AOP实现它。
配置AOP切面
首先我们定义切点:
我们通过@annotation来配置切点,代表我们的AOP切面会切到所有用EagleEye注解修饰的类。
接下来我们利用@Around环绕增强来实现我们的功能:- 首先我们先记录请求链接、接口描述、请求类型、请求IP、请求入参信息
- 接着执行我们注解切到的原方法逻辑
- 最后我们记录整个请求耗时、返回结果
OK,到这里,我们就完成了利用AOP自定义注解的所有步骤。
怎么使用自定义注解?
修饰Controller里的接口方法:
修饰Service里的方法:
对于需要AOP增强的方法,我们只需要:- 在方法上加上@EagleEye注解
- 通过desc元素设置方法的描述
接下来启动应用,请求接口看一下控制台输出是不是像我们开头贴出的那样吧。
-
java 通过AOP记录日志
2021-11-29 16:13:534.3 通过AOP记录操作日志 4.3.1 自定义注解 通过自定义注解,来标示方法需不需要进行记录日志,如果该方法在访问时需要记录日志,则在该方法 上标示该注解既可。 表结构 CREATE TABLE `operation_log` ( `id` bigint...定义一个注解
4.3 通过AOP记录操作日志
4.3.1 自定义注解
通过自定义注解,来标示方法需不需要进行记录日志,如果该方法在访问时需要记录日志,则在该方法 上标示该注解既可。表结构
CREATE TABLE `operation_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `operate_class` varchar(200) DEFAULT NULL COMMENT '操作类', `operate_method` varchar(200) DEFAULT NULL COMMENT '操作方法', `return_class` varchar(200) DEFAULT NULL COMMENT '返回值类型', `operate_user` varchar(20) DEFAULT NULL COMMENT '操作用户', `operate_time` varchar(20) DEFAULT NULL COMMENT '操作时间', `param_and_value` varchar(500) DEFAULT NULL COMMENT '请求参数名及参数值', `cost_time` bigint(20) DEFAULT NULL COMMENT '执行方法耗时, 单位 ms', `return_value` varchar(200) DEFAULT NULL COMMENT '返回值', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
自定义注解
@Inherited @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperateLog { }
4.3.2 定义通知类
@Component @Aspect public class OperateAdvice { private static Logger log = Logger.getLogger(OperateAdvice.class); @Autowired private OperationLogService operationLogService; // 添加日志的service接口 // 利用SpringAOP的环绕通知 cn.itcast.controller包下面的所有类所有方法,并且方法上面有@operateLog注解 @Around("execution(* cn.itcast.controller.*.*(..)) && @annotation(operateLog)") public Object insertLogAround(ProceedingJoinPoint pjp , OperateLog operateLog) throws Throwable{ System.out.println(" ************************ 记录日志 [start] ****************************** "); OperationLog op = new OperationLog(); //记录日志的实体类 DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); op.setOperateTime(sdf.format(new Date())); op.setOperateUser(DataUtils.getRandStr(8)); // 操作人 op.setOperateClass(pjp.getTarget().getClass().getName());// 操作的类 op.setOperateMethod(pjp.getSignature().getName()); // 操作方法 //获取方法调用时传递的参数 Object[] args = pjp.getArgs(); op.setParamAndValue(Arrays.toString(args)); long start_time = System.currentTimeMillis(); //放行 Object object = pjp.proceed(); long end_time = System.currentTimeMillis(); op.setCostTime(end_time - start_time); if(object != null){ op.setReturnClass(object.getClass().getName()); op.setReturnValue(object.toString()); }else{ op.setReturnClass("java.lang.Object"); op.setParamAndValue("void"); } log.error(JsonUtils.obj2JsonString(op)); operationLogService.insert(op);// 插入日志 System.out.println(" ************************** 记录日志 [end] *************************** "); return object; } }
应用
-
一看就会的使用AOP记录日志
2022-05-06 15:48:20一 AOP的含义简介 在开始正式讲解之前,大家一定要明确AOP的具体含义是什么 A : Alone O : OverWatch P : Play AOP的含义呢,大概就是独自(Alone)游玩(Play)守望先锋(OverWatch)这款游戏,众所周知《守望先锋》... -
Aop记录日志实践(获取请求参数&返回参数&环绕通知)
2021-05-31 18:57:21if (i > 0) { logger.info("AOP 记录日志操作成功,操作批次号>>>>>>>>{}", retCode); } else { logger.info("AOP 记录日志操作失败,操作批次号>>>>>>>>{}", retCode); } } }catch (Exception e){ logger.info(... -
spring aop记录日志存储在数据库
2021-11-09 12:35:18使用AOP记录日志存储在数据库第一步 创建日志表log第二步 定义切面类LogAspect第三步 配置JDBCTemplate和aop:aspect第四步 测试运行 第一步 创建日志表log 第二步 定义切面类LogAspect package com.cml.aspect; ... -
SpringMVC使用AOP记录日志
2019-03-05 15:46:48创建AOP @Aspect @Component ... //本地日志记录对象 private static final Logger logger = LoggerFactory.getLogger(LoggerAop.class); //controller切点 @Pointcut("execution(* x... -
Spring:Aop记录日志
2021-01-15 10:40:47Spring:Aop记录日志 本博客默认读者已经了解Aop原理,有spring基础,不在此详细描述。 AOP简介 AOP(Aspect Oriented Programming),即面向切面编程。 (OOP:Object 面向对象编程) 有了AOP,你写代码时不需要把这... -
springBoot项目,使用AOP记录日志无效问题
2020-08-12 16:17:16最近在做一个springboot的项目,当我想要使用aop做一个统一的日志管理的时候,刚开始是可以使用的,但是后来不知道改了什么地方,这个aop日志就失灵。在网上找了很多文章,都没有解决,后来,仔细分析了一下,终于... -
利用spring AOP记录用户操作日志的方法示例
2020-08-30 01:01:59主要给大家介绍了关于利用spring AOP记录用户操作日志的相关资料,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着小编一起来学习学习吧。 -
AOP记录操作日志
2022-04-22 13:30:35使用spring的aop结合自定义注解实现操作日志的记录 首先引入依赖 <!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop... -
springaop实现日志记录
2022-06-02 12:26:03springaop实现日志记录 -
Aop实现日志记录
2021-09-19 22:57:21利用AOP实现日志记录 本文章仅用于学习记录,学习的原文章链接在下方,需要的同学可以去看看。链接: https://blog.csdn.net/m0_37459380/article/details/82696867 需求分析 针对数据库的操作记录进行日志记录,可以... -
SpringBoot利用AOP记录系统日志
2022-05-07 14:45:48利用Spring框架中aop,我们可以实现业务代码与系统级服务进行解耦,例如日志记录、事务及其他安全业务等,可以使得我们的工程更加容易维护、优雅。 一、添加依赖 <dependency> <groupId>org.spring... -
Spring AOP记录日志与事物回滚冲突
2016-03-05 13:40:08<aop:aspect id="sysMonitor" ref="aspectBean" order="2"> <aop:pointcut id="exceptionPointcut" expression="execution(* com.test.*.*.*.*(..))" /> <aop:after-throwing pointcut-ref=... -
springBoot通过aop实现日志记录
2022-04-21 14:31:031. 注解类 import java.lang.annotation.ElementType; import java.lang.annotation.Retention;... * 平台登录日志注解 * @author yangJing */ @Target(ElementType.METHOD) @Retention(Retenti -
使用AOP实现日志记录
2022-02-21 19:44:54使用AOP实现日志记录 -
Spring AOP 记录日志
2016-12-19 17:09:25日志记录问题引子: 在通常开发接口中,经常要记录日志。但是如果这些代码直接写在代码中,后期维护太难,而且也会造成过多的冗余代码。 现在可以使用spring的aop来实现这个功能: 疑问: 1、我们不是所有的... -
SpringBoot+AOP实现日志记录
2022-01-12 16:58:04SpringBoot+AOP实现日志记录,日志入库 -
Spring Boot 使用 AOP 记录日志(简单到爆炸)
2021-03-03 15:52:54这肯定是不合适的,这样的操作无疑是加大了开发量,而且不易维护,所以实际项目中总是利用AOP(Aspect Oriented Programming)即面向切面编程来记录系统中的操作日志。 下面就来介绍如何在 Spring Boot 中 使用 AOP ... -
【Spring学习】AOP实现日志记录
2022-03-21 17:17:36AOP,面向切面编程。通过预编译方式和运行时动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。 -
Spring框架实现AOP添加日志记录功能过程详解
2020-08-25 05:05:53主要介绍了Spring框架实现AOP添加日志记录功能过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
SpringAop实现日志记录功能
2021-05-23 19:00:02AOP(面向切面编程) AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,... -
SpringBoot使用AOP记录接口操作日志
2022-06-12 11:54:05**前言:**我们项目中可能有这种需求,每个人请求了哪些接口?...日志记录量是很大的,所以只记录关键地方并按期归档,最好是存在如elasticsearch中;如果存在数据库中,分表是不错的选择。系统日志:系统 -
aop的应用使用aop完成日志的记录
2019-06-03 16:22:20前面已经介绍过aop的多种应用场景,今天研究了一下使用aop记录日志,使用的是后置通知。具体的实现如下: 自定义注解,用来记录用户的操作和一些基本信息 package com.soecode.lyf.log.annatation; import java.... -
SpringAop 实现记录操作日志
2021-12-03 15:01:55SpringAop 实现记录操作日志 -
Spring使用AOP记录日志(javaconfig配置)
2018-01-14 18:16:18需求:使用spring AOP来记录返回数据日志及异常情况的返回 引入jar 本实例基于spring + maven项目,具体spring maven项目配置就不赘述了,网上有好多资料可供参考,需引入的相关jar如下: dependency> ... -
SpringBoot 使用 AOP 记录接口操作日志
2022-04-20 19:16:38SpringBoot 使用 AOP 记录接口操作日志 一、AOP简介 1.什么是AOP AOP:Aspect Oriented Programming 面向切面编程 AOP关注不是某一个类或某些方法;控制大量资源,关注的是大量的类和方法。 2.AOP应用场景以及常用...