SpringBoot之自定义注解及Java反射机制实现对实体类某些字段记录日志的功能
文章目录
- SpringBoot之自定义注解及Java反射机制实现对实体类某些字段记录日志的功能
- 1. 使用场景
- 2. 实现思路
- 3. 具体实现
- 1. 定义注解类
- 2. 日志实体类
- 3. 反射操作工具类
- 4. 定义记录日志的工具类
- 5. 业务实体中标注自定义注解
- 6. 业务Controller中使用
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 = "一般操作";@Autowiredprivate 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. 业务实体中标注自定义注解
如在角色操作中,对
roleName
与roleCode
记录日志
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 {@Autowiredprivate 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();}}}