自定义SpringBoot切面aop日志

tech2026-02-16  0

1.自定义注解

package cn.wwkj.pms.basis.annotation; import cn.wwkj.pms.basis.entity.vo.EnumLogCode; import java.lang.annotation.*; /** * 日志注解 日志记录注解使用方法@SysLogAnno(value = "添加年份",operateType = EnumLogCode .SYS) * @author GaoDongYang * @date 2020/8/12 14:33 */ @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface SysLogAnno { /** * 操作内容 * @return String */ String value() default ""; /** * 日志code类型 * @return EnumLogCode */ EnumLogCode operateType() default EnumLogCode.SYS; } @Target({ElementType.METHOD}) 注解 表明作用在方法上的注解 ElementType 是一个枚举类型的常量提供了一个简单的分类:表示你自定义的注解可能出现在Java程序中的语法位置,常用的有METHOD、PARAMETER 可用位置和类型有: package java.lang.annotation; /** 1. The constants of this enumerated type provide a simple classification of the 2. syntactic locations where annotations may appear in a Java program. These 3. constants are used in {@link Target java.lang.annotation.Target} 4. meta-annotations to specify where it is legal to write annotations of a 5. given type. 6. @author Joshua Bloch 7. @since 1.5 8. @jls 9.6.4.1 @Target 9. @jls 4.1 The Kinds of Types and Values */ public enum ElementType { /** 类, 接口 (包括注释类型), 或 枚举 声明 */ TYPE, /** 字段声明(包括枚举常量) */ FIELD, /** 方法声明(Method declaration) */ METHOD, /** 正式的参数声明 */ PARAMETER, /** 构造函数声明 */ CONSTRUCTOR, /** 局部变量声明 */ LOCAL_VARIABLE, /** 注释类型声明 */ ANNOTATION_TYPE, /** 包声明 */ PACKAGE, /** * 类型参数声明 * * @since 1.8 */ TYPE_PARAMETER, /** * 使用的类型 * * @since 1.8 */ TYPE_USE } @Retention({RetentionPolicy.Runtime}) 注解 RetentionPolicy这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间,一般Runtime package java.lang.annotation; /** 1. Annotation retention policy. The constants of this enumerated type 2. describe the various policies for retaining annotations. They are used 3. in conjunction with the {@link Retention} meta-annotation type to specify 4. how long annotations are to be retained. 5. 6. @author Joshua Bloch 7. @since 1.5 */ public enum RetentionPolicy { /** * 注释只在源代码级别保留,编译时被忽略 */ SOURCE, /** * 注释将被编译器在类文件中记录 * 但在运行时不需要JVM保留。这是默认的 * 行为. */ CLASS, /** *注释将被编译器记录在类文件中 *在运行时保留VM,因此可以反读。 * @see java.lang.reflect.AnnotatedElement */ RUNTIME }

@Documented注解 就是一个声明

Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

2.引入aop依赖

pom文件中

<!--spring切面aop依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

3.自定义切面类

package cn.wwkj.pms.basis.aspect; import cn.wwkj.pms.basis.annotation.SysLogAnno; import cn.wwkj.pms.basis.entity.bo.Log; import cn.wwkj.pms.basis.entity.vo.EnumLogCode; import cn.wwkj.pms.basis.service.SysLogService; import com.fasterxml.jackson.databind.ObjectMapper; 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.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 日志切面 * @author gaodongyang * @date 2020/8/12 14:37 **/ @Aspect @Component @Order(0) @Slf4j public class LogAspect { private final SysLogService sysLogService; private final ObjectMapper objectMapper; /** * 构造注入 * @param sysLogService 日志 * @param objectMapper json序列化映射器 */ public LogAspect(SysLogService sysLogService, ObjectMapper objectMapper) { this.sysLogService = sysLogService; this.objectMapper = objectMapper; } /** * 设置切点 对有@SysLogAnno注释的方法生效 * @author gaodongyang * @date 2020/8/12 14:42 **/ @Pointcut("@annotation(cn.wwkj.pms.basis.annotation.SysLogAnno)") public void logPointCut() { } /** * 环绕通知 * @author gaodongyang * @date 2020/8/12 16:47 * @param point 连接点 * @return Object 结果 **/ @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { log.info("环绕通知开始。。。。。"); //日志 Log sysLog = new Log(); Object result = null; try { // 执行方法 result = point.proceed(); sysLog.setRet(0); }catch (Exception e){ log.error(e.getMessage(),e); sysLog.setRet(1) .setRetStr(e.getMessage()); }finally { //从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature = (MethodSignature) point.getSignature(); //获取切入点所在的方法 Method method = signature.getMethod(); // 获得注解 操作 SysLogAnno monitorLog = method.getAnnotation(SysLogAnno.class); // 获得code EnumLogCode operateType = monitorLog.operateType(); // 获得操作内容 String value = monitorLog.value(); System.out.print(result); sysLog.setCont1(value); //请求的参数 Object[] args = point.getArgs(); //将参数所在的数组转换成json StringBuilder params = new StringBuilder(); for(Object o : args){ params.append(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(o)); } sysLog.setRemark1(params.toString()); //添加日志 sysLogService.add(operateType,sysLog); log.info("环绕通知结束。。。。。"); } return result; } }

4.日志有关数据库,这里只写插入方法

日志插入service层的add方法,主要就是在本方法上加上**@Transactional(propagation = Propagation.NOT_SUPPORTED)**事务,防止在其他方法上加日志注解时,因其方法内部的错误导致事务回归,日志也回滚,不能在其他方法执行失败时把日志插入数据库中。

/** * 添加日志 * @param logParam 日志字段实体 * @author GaoDongYang, GongLiHai * @date 2020/8/11 10:38 **/ @Override @Transactional(propagation = Propagation.NOT_SUPPORTED) public void addLog(Log logParam) { //初始化日志对象,并设置设备信息,用户信息,ip等内容 SysLog sysLog = new SysLog(logParam) .setDevice(getDevice(request.getHeader("User-Agent"))) .setUserDept(CurrentUserService.currentUserDetails()) .setIp(((WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getRemoteAddress()) .setInParam(getFmtInParam(logParam.getInParam())); sysLogMapper.insert(sysLog); }

5.使用时,只需加上注解

/** * 添加要素详情 * @param elementDetail 要添加的要素详情信息 * @param year 年份 * @return 返回添加结果 * @author WeiJian **/ @RequestMapping("/addElementDetail") @SysLogAnno(value = "添加年份",operateType = EnumLogCode .SYS) public Result<?> addElementDetail(@Valid ElementDetail elementDetail, @CurrentYear int year) { elementDetail.setYear(year); return ResultUtil.updateResult(elementDetailService.addElementDetail(elementDetail)); }

6.我定义的注解中operateType = EnumLogCode .SYS时枚举类型

也可不用枚举,传字符串什么的都一样

package cn.wwkj.pms.basis.entity.vo; /** * 日志 code name 枚举类 * @author gaodongyang * @date 2020/8/11 10:00 **/ @SuppressWarnings("unused") public enum EnumLogCode { //code数据库映射 SYS("001","系统日志"), SYS_LOGIN("001001","登录日志"); private String name; private String code; EnumLogCode(String code, String name){ this.name = name; this.code = code; } public String getName() { return name; } public String getCode() { return code; } }
最新回复(0)