Spring AOP利用切面实现日志保存的示例详解
陶陶吖 人气:0依赖引入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
创建表
DROP TABLE IF EXISTS `logger_record`; CREATE TABLE `logger_record` ( `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'id', `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `package_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作所在的包', `class_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作的类名', `method_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作的方法名', `request_params` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '操作方法的参数', `response_params` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '操作方法的出参', `running_time` bigint NULL DEFAULT NULL COMMENT '运行时间', `created_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人ID', `date_created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
对象实体类
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.**.app.common.util.TimeUtils; import lombok.Data; import org.springframework.data.annotation.Id; import java.io.Serializable; import java.time.LocalDateTime; import java.util.Date; /** * Created by cb on 2022/11/15 10:07 */ //@Entity @Data public class LoggerRecord implements Serializable { @Id @TableId(value = "id",type = IdType.ID_WORKER_STR)//可不引入注解,UUID生成 private String id; /** * 操作方法的描述 */ private String description;//实际没用上 /** * 操作所在的包 */ private String packageName; /** * 操作的类名 */ private String className; /** * 操作的方法名 */ private String methodName; /** * 操作方法的参数 */ private String requestParams; /** * 操作方法的出参 */ private String responseParams; /** * 运行时间 */ private Long runningTime; /** 创建时间 */ @JsonFormat(pattern = TimeUtils.PATTERN2) private LocalDateTime dateCreated;//可以用date /** 创建人ID */ private String createdBy;//实际没用上 }
注解方法
package com.**.app.server.annotation; import java.lang.annotation.*; /** * Created by cb on 2022/11/15 10:05 * 日志描述的注解 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LoggerManager { }
service方法
可根据自己项目去相应改动
/** * 日志增删改的接口 * Created by cb on 2022/11/15 10:11 */ @Service public class LoggerRecordRepository extends ServiceImpl<LoggerRecordMapper, LoggerRecord> { }
工具类
package com.**.app.server.util; import org.apache.commons.lang.builder.ToStringBuilder; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * Created by cb on 2022/11/15 10:24 */ public class AopUtil { /** * 获取切点处的方法 * @param joinPoint * @return */ public static Method getMethod(JoinPoint joinPoint){ MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); return method; } /** // * 将参数数组转化为字符串 // * @param params 参数数组 // * @return 参数字符串 // */ public static String getStringOfParams(Object[] params) { if (params.length <= 0 || params.length > 1024 || null == params) { return ""; } StringBuffer paramString = new StringBuffer("参数: "); for (Object param : params) { //将参数转换成字符串 String s = ToStringBuilder.reflectionToString(param); paramString.append(s).append("||"); } return paramString.toString(); } /** * 转换request 请求参数 * @param paramMap request获取的参数数组 */ public static Map<String, String> converMap(Map<String, String[]> paramMap) { Map<String, String> rtnMap = new HashMap<String, String>(); for (String key : paramMap.keySet()) { rtnMap.put(key, paramMap.get(key)[0]); } return rtnMap; } }
注解实现方法
package com.**.app.server.annotation; import com.alibaba.fastjson.JSON; import com.**.app.server.entity.log.LoggerRecord; import com.**.app.server.service.LoggerRecordRepository; import com.**.app.server.util.AopUtil; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Map; /** * Created by cb on 2022/11/15 10:19 */ @Aspect @Service @Slf4j public class LoggerAdvice { @Resource private LoggerRecordRepository loggerRecordRepository; /** * 切点,在注解处切入 */ @Pointcut("@annotation(com.safety.app.server.annotation.LoggerManager)") public void AroundPointCut(){ } /** * 环绕通知 @Around , 当然也可以使用 @Before (前置通知) @After (后置通知) * @param point * @return * @throws Throwable */ @Around("AroundPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); Object result = point.proceed(); long time = System.currentTimeMillis() - beginTime; try { LoggerRecord loggerRecord = saveLog(point, time); loggerRecord.setResponseParams(JSON.toJSONString(result)); loggerRecordRepository.save(loggerRecord); } catch (Exception e) { }finally { return result; } } /** * 保存日志 * @param joinPoint * @param time */ private LoggerRecord saveLog(ProceedingJoinPoint joinPoint, long time) { // 获取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // 从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //获取该切点处的方法 Method method = AopUtil.getMethod(joinPoint); LoggerRecord loggerRecord = new LoggerRecord(); //获取切点处的注解 LoggerManager loggerManager = method.getAnnotation(LoggerManager.class); if (null == loggerManager) { return loggerRecord; } //获取请求的描述 // String description = loggerManager. //获取请求包名 Signature signature = joinPoint.getSignature(); //获取请求的类名 String className = joinPoint.getTarget().getClass().getName(); //获取请求的方法名 String methodName = method.getName(); //获取请求的参数 Map<String, String[]> parameterMap = request.getParameterMap(); Map<String, String> stringStringMap = AopUtil.converMap(parameterMap); log.info("执行===" + methodName + "===开始"); //打印方法名 log.info("方法名:" + signature.toString()); //打印方法参数 log.info("方法参数:" + JSON.toJSONString(stringStringMap)); log.info("执行===" + methodName + "===结束"); //将日志保存 // loggerRecord.setDescription(description); loggerRecord.setPackageName(signature.toString()); loggerRecord.setClassName(className); loggerRecord.setMethodName(methodName); loggerRecord.setRunningTime(time); loggerRecord.setRequestParams(JSON.toJSONString(stringStringMap)); return loggerRecord; } }
最后只需要在自己的接口上加注解@LoggerManager就可以实现此方法的入参出参日志保存
/** * 测试 */ @GetMapping("/test") @ResponseBody @LoggerManager public ResponseResult test(@RequestParam("userName") String userName) throws BusinessException { Map<String, Object> verifyCodeMap = VerifyCodeUtils.verifyCode(); redisUtils.set(VERIFY_CODE + userName, verifyCodeMap, 60); return new ResponseResult(verifyCodeMap); }
保存数据库后的图片:
注:所有‘**’的地方为自己项目的包名。 好了,下期会改版此日志保存方法,在数据库配置接口名称 就可以实现日志的打印保存。
加载全部内容