SpringBoot之自定义注解及Java反射机制实现对实体类某些字段记录日志的功能
文章目录
1. 使用场景
2. 实现思路
3. 具体实现
1. 定义注解类
定义一个名称为LogModel
的注解
package com.yuan.annotation;
import java.lang.annotation.*;
/**
* <p>
*
* @Description: 日志记录模型注解 <br>
* <p>
* @Author: Yuan · JinSheng <br>
* @Datetime: 2022-04-07 14:27
* </p>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface LogModel {
/**
* 模块名称
*/
String moduleName() default "系统管理";
/**
* 菜单名称
*/
String menuName() default "";
/**
* 功能描述
* 不设置default默认值,则使用时必须为此属性赋值
*/
String functionDesc();
/**
* 操作
* 不设置default默认值,则使用时必须为此属性赋值
*/
String operation();
/***
* 操作内容
*/
String operationContent() default "";
}
2. 日志实体类
package com.yuan.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <p>
* Description: 系统日志
* </p>
*
* @author
* @datetime
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("SYS_LOG")
public class Syslog implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
//....
private String moduleName;
private String menuName;
private String functionDesc;
private String operation;
private String operationContent;
// ...
}
3. 反射操作工具类
package com.yuan.util;
import com.yuan.annotation.LogModel;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
*
* @Description: 反射工具类 <br>
* <p>
* @Author: Yuan · JinSheng <br>
* @Datetime: 2022-04-07 15:27
* </p>
*/
public class ReflectionUtil<T> {
/**
* @param entity 实体对象
* @param contentSeparator 操作内容分割符
* @return Map对象
* @throws IllegalAccessException
* @Description: 根据自定义@LogName注解设置记录日志的相关属性
*/
public Map<String, String> setLogModel(T entity, String contentSeparator) throws IllegalAccessException {
Map<String, String> objectMap = new HashMap<>();
List<String> operationContentList = new ArrayList<>();//操作内容集合
//获取所有的属性
Field[] fields = entity.getClass().getDeclaredFields();
for (Field field : fields) {
//是否存在LogName注解
boolean annotationPresent = field.isAnnotationPresent(LogModel.class);
//存在LogName注解
if (annotationPresent) {
LogModel annotation = field.getAnnotation(LogModel.class);
field.setAccessible(true);//设置可访问私有属性
objectMap.put("moduleName", annotation.moduleName());//模块名
objectMap.put("menuName", annotation.menuName());//菜单名
objectMap.put("function", annotation.function());//操作
objectMap.put("functionDesc", annotation.functionDesc());//操作描述
operationContentList.add(annotation.operationContent() + ":" + field.get(entity));//拼接操作操作内容以:分割
}
}
objectMap.put("operationContent", String.join(contentSeparator, operationContentList));//操作内容
return objectMap;
}
}
4. 定义记录日志的工具类
package com.yuan.util;
import com.yuan.util.UUIDGenerator;
import com.yuan.service.SyslogService;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* <p>
*
* @Description: 日志记录工具类 <br>
* <p>
* @Author: Yuan · JinSheng <br>
* @Datetime: 2022-04-07 15:27
* </p>
*/
@Component
public class LogModelUtil<T> {
//操作成功
public static final String OPERATION_SUCCESS = "操作成功";
//操作失败
public static final String OPERATION_ERROR = "操作失败";
//新增
public static final String ADD = "新增";
//删除
public static final String DELETE = "删除";
//查询
public static final String QUERY = "查询";
//修改
public static final String UPDATE = "修改";
//导入
public static final String IMPORT = "导入";
//导出
public static final String EXPORT = "导出";
//上传
public static final String UPLOAD = "上传";
//下载
public static final String DOWNLOAD = "下载";
//出现异常
public static final String EXCEPTION = "出现异常";
//一般操作
public static final String GENERAL_OPERATION = "一般操作";
@Autowired
private SyslogService syslogService;
/**
* <p>
* Description: 记录日志新方法<br/>
* Author: jinshengyuan <br/>
* DateTime: 2020-04-08 16:11
* </p>
*
* @param entityList 实体对象集合
* @param operationType 操作类型
* @param operationResult 操作结果
*/
public void insertLog(List<T> entityList, String operationType, String operationResult) {
Syslog syslog = new Syslog();
//方法操作类
ReflectionUtil reflectionUtil = new ReflectionUtil();
//操作内容
List<String> contentList = new ArrayList<>();
//取每一个对象中加了@LogName注解的属性值
for (T entity : entityList) {
try {
Map<String, String> map = reflectionUtil.setLogName(entity, ",");
syslog.setModuleName(map.get("moduleName"));
syslog.setMenuName(map.get("menuName"));
syslog.setFunctionDesc(map.get("functionDesc"));
syslog.setOperation(map.get("operation"));
contentList.add(map.get("operationContent"));
} catch (IllegalAccessException e) {
//e.printStackTrace();
continue;
}
}
//有操作内容
if (contentList.size() > 0) {
String content = String.join(";", contentList);
//1.如果操作内容长度大于2000则拆分成多条存储
if (content.length() > 2000) {
List<String> strList = new ArrayList<>();
strList = substringStrListByLength(content, strList, 2000);
strList.forEach(str -> {
syslog.setId(UUIDGenerator.getUUID());//主键
syslog.setOperationContent(str);//操作内容
syslogService.save(syslog);
});
} else {//2.操作内容长度小于2000直接存成一条
syslog.setId(UUIDGenerator.getUUID());//主键
syslog.setOperationContent(String.join(";", contentList));//操作内容
syslogService.save(syslog);
}
} else {//操作内容为空字符串,就存空字符
syslog.setId(UUIDGenerator.getUUID());//主键
syslog.setOperationContent("");//操作内容
syslogService.save(syslog);
}
}
private List<String> substringStrListByLength(String str, List<String> cacheList, int strLength) {
if (str.length() > strLength) {
cacheList.add(str.substring(0, strLength));
substringStrListByLength(str.substring(strLength), cacheList, strLength);
} else {
cacheList.add(str);
}
return cacheList;
}
}
5. 业务实体中标注自定义注解
package com.yuan.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* @Description: 角色实体 <br>
* <p>
* @Author: Yuan · JinSheng <br>
* @Datetime: 2022-04-07 15:27
* </p>
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("SYS_ROLE")
public class SysRole implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId("ID")
private String id;
/**
* 角色名称
*/
@TableField("ROLE_NAME")
@LogModel(moduleName = "系统管理测试",menuName = "角色",function = "记录角色名称",functionDesc = "新增操作")
private String roleName;
/**
* 角色编码
*/
@TableField("ROLE_CODE")
@LogModel(moduleName = "系统管理测试",menuName = "角色",function = "记录角色编码",functionDesc = "新增操作")
private String roleCode;
/**
* 创建人
*/
@TableField("CREATE_BY")
private String createBy;
/**
* 创建时间
*/
@TableField("CREATE_TIME")
private Date createTime;
//...
}
6. 业务Controller中使用
package com.yuan.sys.controller;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuan.sys.service.SysroleService;
import com.yuan.entity.Sysrole;
import com.yuan.util.MsgUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
*
* @Description: 角色控制器 <br>
* <p>
* @Author: Yuan · JinSheng <br>
* @Datetime: 2022-04-07 15:27
* </p>
*/
@RestController
@RequestMapping("/sys/sysRole")
public class SysRoleController {
@Autowired
private SysroleService service;
@RequestMapping("/saveBatch")
public MsgUtil saveBatch(@RequestParam("entityJsonList") String entityJsonList){
List<Sysrole> list = JSON.parseObject(entityJsonList, List.class);
if (service.saveBatch(list)) {
//记录日志
LogUtil.insertLog(list, LogUtil.ADD, LogUtil.OPERATION_SUCCESS);
return MsgUtil.success();
} else {
return MsgUtil.fail();
}
}
}