首页 > 其他分享 >3.自定义注解实现系统日志记载

3.自定义注解实现系统日志记载

时间:2023-04-09 14:34:10浏览次数:49  
标签:return String 自定义 void 注解 日志 operate public 系统日志

前言

  今天来分享一下我昨天的成果,昨天计划复现若依系统的系统日志记载功能,若依的系统日志记载的主要实现使用过自定义注解配合切面类来实现的,这里会把标注@Log的方法在用户调用完后,将方法的一部分信息记录在数据库的指定数据表中。因此我们需要java的spring开发四层结构:domain层、mapper层、service层、controller层。到这里项目就大概完成了,注意的是若依中自定义的工具类。本文的项目代码链接:WomPlus: 结合若依项目对原始工单项目内容进行增强 (gitee.com),若依项目链接:GitHub - yangzongzhuan/RuoYi-fast: (RuoYi)官方仓库 基于SpringBoot的权限管理系统 易读易懂、界面简洁美观。 核心技术采用Spring、MyBatis、Shiro没有任何其它重度依赖。直接运行即可用

1.系统日志记载开发流程五步走

  朋友们可以根据自己的项目来调节数据表结构,domain类、mapper接口以及Mapper.xml、Service接口及其实现类,我这里是根据自己项目需求来编写的。

1.1 根据自己项目创建数据表wo_operate_log

USE `wom_plus`
DROP TABLE IF EXISTS `wo_operate_log`
CREATE TABLE `wo_opertae_log`(
    `operate_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '日志主键',
    `title` VARCHAR(50) DEFAULT '' COMMENT '模块标题',
    `business_type` INT(2) DEFAULT 0 COMMENT '业务类型(0其它 1新增 2修改 3删除)',
    `method` VARCHAR(100) DEFAULT '' COMMENT '方法名称',
    `request_method` VARCHAR(10) DEFAULT '' COMMENT '请求方式',
    `operator_type` INT(1) DEFAULT 0 COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
    `operate_name` VARCHAR(50) DEFAULT '' COMMENT '操作人员',
    `operate_url` VARCHAR(255) DEFAULT '' COMMENT '请求URL',
    `operate_ip` VARCHAR(128) DEFAULT '' COMMENT '主机地址',
    `operate_location` VARCHAR(255) DEFAULT '' COMMENT '操作地点',
    `operate_param` VARCHAR(2000) DEFAULT '' COMMENT '请求参数',
    `json_result` VARCHAR(2000) DEFAULT '' COMMENT '返回参数',
    `status` INT(1) DEFAULT 0 COMMENT '操作状态(0正常 1异常)',
    `error_msg` VARCHAR(2000)   DEFAULT ''COMMENT '错误消息',
    `operate_time` DATETIME COMMENT '操作时间',
    `cost_time` BIGINT(20) DEFAULT 0 COMMENT '消耗时间',
    PRIMARY KEY (operate_id),
    KEY idx_sys_oper_log_bt (`business_type`),
    KEY idx_sys_oper_log_s  (`status`),
    KEY idx_sys_oper_log_ot (`operate_time`)
)ENGINE=INNODB COMMENT='操作日志记录' DEFAULT CHARSET='utf8';
wo_operate_log

1.2 根据自己项目编写Operate实体类

/**
 * 操作日志记录表 oper_log
 * 
 * @author ruoyi
 */
public class OperateLog extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 日志主键 */
    @Excel(name = "操作序号", cellType = Excel.ColumnType.NUMERIC)
    private Long operateId;

    /** 操作模块 */
    @Excel(name = "操作模块")
    private String title;

    /** 业务类型 */
    @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
    private Integer businessType;
    
    /** 业务类型数组 */
    private Integer[] businessTypes;

    /** 请求方法 */
    @Excel(name = "请求方法")
    private String method;

    /** 请求方式 */
    @Excel(name = "请求方式")
    private String requestMethod;

    /** 操作人类别 */
    @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
    private Integer operatorType;

    /** 操作人员 */
    @Excel(name = "操作人员")
    private String operateName;

//    /** 部门名称 */
//    @Excel(name = "部门名称")
//    private String deptName;

    /** 请求url */
    @Excel(name = "请求地址")
    private String operateUrl;

    /** 操作地址 */
    @Excel(name = "操作地址")
    private String operateIp;

    /** 操作地点 */
    @Excel(name = "操作地点")
    private String operateLocation;

    /** 请求参数 */
    @Excel(name = "请求参数")
    private String operateParam;

    /** 返回参数 */
    @Excel(name = "返回参数")
    private String jsonResult;

    /** 状态0正常 1异常 */
    @Excel(name = "状态", readConverterExp = "0=正常,1=异常")
    private Integer status;

    /** 错误消息 */
    @Excel(name = "错误消息")
    private String errorMsg;

    /** 操作时间 */
    @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date operTime;

    /** 消耗时间 */
    @Excel(name = "消耗时间", suffix = "毫秒")
    private Long costTime;

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public Long getOperateId() {
        return operateId;
    }

    public void setOperateId(Long operateId) {
        this.operateId = operateId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Integer getBusinessType() {
        return businessType;
    }

    public void setBusinessType(Integer businessType) {
        this.businessType = businessType;
    }

    public Integer[] getBusinessTypes() {
        return businessTypes;
    }

    public void setBusinessTypes(Integer[] businessTypes) {
        this.businessTypes = businessTypes;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getRequestMethod() {
        return requestMethod;
    }

    public void setRequestMethod(String requestMethod) {
        this.requestMethod = requestMethod;
    }

    public Integer getOperatorType() {
        return operatorType;
    }

    public void setOperatorType(Integer operatorType) {
        this.operatorType = operatorType;
    }

    public String getOperateName() {
        return operateName;
    }

    public void setOperateName(String operateName) {
        this.operateName = operateName;
    }

    public String getOperateUrl() {
        return operateUrl;
    }

    public void setOperateUrl(String operateUrl) {
        this.operateUrl = operateUrl;
    }

    public String getOperateIp() {
        return operateIp;
    }

    public void setOperateIp(String operateIp) {
        this.operateIp = operateIp;
    }

    public String getOperateLocation() {
        return operateLocation;
    }

    public void setOperateLocation(String operateLocation) {
        this.operateLocation = operateLocation;
    }

    public String getOperateParam() {
        return operateParam;
    }

    public void setOperateParam(String operateParam) {
        this.operateParam = operateParam;
    }

    public String getJsonResult() {
        return jsonResult;
    }

    public void setJsonResult(String jsonResult) {
        this.jsonResult = jsonResult;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public Date getOperateTime() {
        return operTime;
    }

    public void setOperateTime(Date operateTime) {
        this.operTime = operateTime;
    }

    public Long getCostTime() {
        return costTime;
    }

    public void setCostTime(Long costTime) {
        this.costTime = costTime;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("operateId", getOperateId())
            .append("title", getTitle())
            .append("businessType", getBusinessType())
            .append("businessTypes", getBusinessTypes())
            .append("method", getMethod())
            .append("requestMethod", getRequestMethod())
            .append("operatorType", getOperatorType())
            .append("operateName", getOperateName())
            .append("operateUrl", getOperateUrl())
            .append("operateIp", getOperateIp())
            .append("operateLocation", getOperateLocation())
            .append("operateParam", getOperateParam())
            .append("status", getStatus())
            .append("errorMsg", getErrorMsg())
            .append("operateTime", getOperateTime())
            .append("costTime", getCostTime())
            .toString();
    }
}
OperateLog

1.3 根据自己项目编写OperateMapper和OperateMapper.xml

public interface OperateLogMapper {
    /**
     * 新增操作日志
     * @param :operateLog 操作日志对象
     */
    public void insertOperateLog(OperateLog operateLog);

    /**
     * 查询系统操作日志集合
     * @param :operateLog 操作日志对象
     * @return 操作日志集合
     */
    public List<OperateLog> selectOperateLogList(OperateLog operateLog);

    /**
     * 批量删除系统操作日志
     * @param ids 需要删除的数据
     * @return 结果
     */
    public int deleteOperateLogByIds(String[] ids);

    /**
     * 查询操作日志详细
     * @param :operateId 操作ID
     * @return 操作日志对象
     */
    public OperateLog selectOperateLogById(Long operateId);

    /**
     * 清空操作日志
     */
    public void cleanOperateLog();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ku.wo.project.system.monitor.operlog.mapper.OperateLogMapper">

    <resultMap id="OperateLogResult" type="com.ku.wo.project.system.monitor.operlog.domain.OperateLog" >
        <id     property="operateId" column="operate_id"/>
        <result property="title" column="title"/>
        <result property="businessType" column="business_type"/>
        <result property="method" column="method"/>
        <result property="requestMethod" column="request_method"/>
        <result property="operatorType" column="operator_type"/>
        <result property="operateName" column="oper_name"/>
        <result property="operateUrl" column="oper_url"/>
        <result property="operateIp" column="oper_ip"/>
        <result property="operateLocation" column="oper_location"/>
        <result property="operateParam" column="oper_param"/>
        <result property="jsonResult" column="json_result"/>
        <result property="status" column="status"/>
        <result property="errorMsg" column="error_msg"/>
        <result property="operateTime" column="operate_time"/>
        <result property="costTime" column="cost_time"/>
    </resultMap>

    <sql id="selectOperateLogVo">
        select operate_id, title, business_type, method, request_method, operator_type, operate_name, operate_url, operate_ip, operate_location, operate_param, json_result, status, error_msg, operate_time, cost_time
        from wom_plus.wo_operate_log
    </sql>

    <!--keyProperty="operateId"中的值为属性值-->
    <insert id="insertOperateLog" useGeneratedKeys="true" keyProperty="operateId">
      insert into wom_plus.wo_operate_log(title, business_type, method, request_method, operator_type, operate_name, operate_url, operate_ip, operate_location, operate_param, json_result, status, error_msg, cost_time, operate_time)
        values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operateName}, #{operateUrl}, #{operateIp}, #{operateLocation}, #{operateParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())
   </insert>

    <select id="selectOperateLogList" resultMap="OperateLogResult">
        <include refid="selectOperateLogVo"/>
        <where>
            <if test="title != null and title != ''">
                AND title like concat('%', #{title}, '%')
            </if>
            <if test="businessType != null">
                AND business_type = #{businessType}
            </if>
            <if test="businessTypes != null and businessTypes.length > 0">
                AND business_type in
                <foreach collection="businessTypes" item="businessType" open="(" separator="," close=")">
                    #{businessType}
                </foreach>
            </if>
            <if test="status != null">
                AND status = #{status}
            </if>
            <if test="operateName != null and operateName != ''">
                AND operate_name like concat('%', #{operateName}, '%')
            </if>
            <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
                AND operate_time &gt;= #{params.beginTime}
            </if>
            <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
                AND operate_time &lt;= #{params.endTime}
            </if>
        </where>
    </select>

    <delete id="deleteOperateLogByIds">
        delete from wom_plus.wo_operate_log where operate_id in
        <foreach collection="array" item="operateId" open="(" separator="," close=")">
            #{operateId}
        </foreach>
    </delete>

    <select id="selectOperateLogById" resultMap="OperateLogResult">
        <include refid="selectOperateLogVo"/>
        where operate_id = #{operateId}
    </select>

    <!--清空一个表-->
    <update id="cleanOperateLog">
        truncate table wo_operate_log
    </update>

</mapper>
Mapper接口及其SQL

1.4 根据自己项目编写IOperateService和OperateServiceImpl

public interface IOperateLogService {
    /**
     * 新增操作日志
     *
     * @param :operateLog 操作日志对象
     */
    public void insertOperateLog(OperateLog operateLog);

    /**
     * 查询系统操作日志集合
     *
     * @param :operateLog 操作日志对象
     * @return 操作日志集合
     */
    public List<OperateLog> selectOperateLogList(OperateLog operateLog);

    /**
     * 批量删除系统操作日志
     *
     * @param ids 需要删除的数据
     * @return 结果
     */
    public int deleteOperateLogByIds(String ids);

    /**
     * 查询操作日志详细
     *
     * @param operateId 操作ID
     * @return 操作日志对象
     */
    public OperateLog selectOperateLogById(Long operateId);

    /**
     * 清空操作日志
     */
    public void cleanOperateLog();
}
@Service
public class OperateLogServiceImpl implements IOperateLogService {

    @Autowired(required = false)
    private OperateLogMapper operateLogMapper;

    @Override
    public void insertOperateLog(OperateLog operateLog) {
        operateLogMapper.insertOperateLog(operateLog);
    }

    @Override
    public List<OperateLog> selectOperateLogList(OperateLog operateLog) {
        return operateLogMapper.selectOperateLogList(operateLog);
    }

    @Override
    public int deleteOperateLogByIds(String ids) {
        return deleteOperateLogByIds(ids);
    }

    @Override
    public OperateLog selectOperateLogById(Long operateId) {
        return operateLogMapper.selectOperateLogById(operateId);
    }

    @Override
    public void cleanOperateLog() {
        operateLogMapper.cleanOperateLog();
    }
}
IoperateService及其实现类

1.5 Controller方法上的@Log

@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(@RequestParam(value = "name", required = false) String username){
    List<SysUser> list = userDetailsService.getUserListByUsername(username);
    ExcelUtil<SysUser> util = new ExcelUtil<>(SysUser.class);
    return util.exportExcel(list, "用户数据");
}

2.自定义系统日志记载注解及其切面实现

  java中注解与AOP的结合,方便了广大java程序员对应用的开发,能够对原有方法的增强减少很多代码,原来的我们如果要在每个方法上面进行日志记载,那么需要每个方法都调用日志记载的方法,而现在我们只需要在需要日志加载的方法上面加上@ Log就完美快速简单地解决了上述繁杂问题。

2.1 自定义@Log

/**
 * 自定义操作日志记录注解
 * 
 * @author ruoyi
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })//作用于方法和参数上面
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
    /**
     * 模块
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;

    /**
     * 排除指定的请求参数
     */
    public String[] excludeParamNames() default {};
}
@Log

2.2 LogAspect(重点)

  该类就是根据动态代理来实现的,在Spring中称之为AOP,面向切面编程,可以很好地实现对软件中已有方法在安全、日志、监控等方面的增强。

@Component
@Aspect
/**
 * 操作日志记录处理
 */
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /** 排除敏感属性字段 */
    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    //该方法传入一个注解类型参数,改参数被@Before注解中的@annotation作用,
    // 表示只要该注解作用在哪个方法上,就在该方法上生效
    public void before(JoinPoint joinPoint, Log controllerLog){
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    /**
     * 处理完请求后执行
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
    {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
    {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog,
                             final Exception e, Object jsonResult){
        try
        {
            //本项目没有使用shiro框架,所以无法根据登录获取用户信息,以后再完善
//            // 获取当前的用户
//            SysUser currentUser = ShiroUtils.getSysUser();

            // *========数据库日志=========*//
            OperateLog operateLog = new OperateLog();
            //ordinal可以返回当前枚举所在的序列,利用这个函数,可以自增长的获取我们定义的Excel的cell位置,
            // 然后进行写入数据操作
            operateLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // 请求的地址
//            String ip = ShiroUtils.getIp();//这里暂时不使用Shiro相关类
            String ip = IPUtils.getIpAddr(ServletUtils.getRequest());
            operateLog.setOperateIp(ip);
            operateLog.setOperateUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
            //后面完善
//            if (currentUser != null)
//            {
//                operateLog.setOperateName(currentUser.getUsername());
//            }
            operateLog.setOperateName("xiaoku");
            if (e != null)
            {
                operateLog.setStatus(BusinessStatus.FAIL.ordinal());
                operateLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operateLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            operateLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operateLog, jsonResult);
            // 设置消耗时间
            operateLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());

            //我这里不使用异步任务,因此在这里执行插入
            //这里通过SpringUtils.getBean(Class<T>clz)来获取所需对象
            // 远程查询操作地点
            operateLog.setOperateLocation(AddressUtils.getRealAddressByIP(operateLog.getOperateIp()));
            SpringUtils.getBean(IOperateLogService.class).insertOperateLog(operateLog);

            //暂时不需要
//            // 保存数据库
//            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
        }
        catch (Exception exp)
        {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
        finally
        {
            TIME_THREADLOCAL.remove();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * @param log 日志
     * @param :operateLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperateLog operLog, Object jsonResult) throws Exception
    {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData())
        {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog, log.excludeParamNames());
        }
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
        {
            operLog.setJsonResult(StringUtils.substring(JSONObject.toJSONString(jsonResult), 0, 2000));
        }
    }

    /**
     * 获取请求的参数,放到log中
     *
     * @param : operateLog
     * @param : request
     */
    private void setRequestValue(JoinPoint joinPoint, OperateLog operLog, String[] excludeParamNames)
    {
        Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
        if (StringUtils.isNotEmpty(map))
        {
            String params = JSONObject.toJSONString(map, excludePropertyPreFilter(excludeParamNames));
            operLog.setOperateParam(StringUtils.substring(params, 0, 2000));
        }
        else
        {
            Object args = joinPoint.getArgs();
            if (StringUtils.isNotNull(args))
            {
                String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
                operLog.setOperateParam(StringUtils.substring(params, 0, 2000));
            }
        }
    }

    /**
     * 忽略敏感属性
     */
    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames)
    {
        return new PropertyPreFilters().addFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray)
            {
                if (StringUtils.isNotNull(o) && !isFilterObject(o))
                {
                    try
                    {
                        Object jsonObj = JSONObject.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o)
    {
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Object value : collection)
            {
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Object value : map.entrySet())
            {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
LogAspect

  上述代码中的前置通知@Before和后置通知@After是用来记录标注有@Log注解的方法运行所花费的时间,环绕返回注解@AroundReturning是返回系统日志记载信息,环绕异常注解@AroundThrowing是返回这部分代码出现异常是返回的异常信息。主要的日志系统信息实现是在handleLog()方法中,根据切点获取方法名称、请求参数、等等信息。

2.3 若依中的自定义工具类

3.项目运行结果

   第一张图的运行结果是使用@Log注解标注在以Excel形式导出数据的export()方法上面,第二张图片是系统日志记载表wo_operate_log记载的执行标有@Log注解的方法的日志记载信息。我这里只截取了后面的内容。

标签:return,String,自定义,void,注解,日志,operate,public,系统日志
From: https://www.cnblogs.com/kzf-99/p/17300290.html

相关文章

  • GitHub Actions:从使用action操作到自定义action操作
    (目录)1、使用action操作文档https://docs.github.com/zh/actions/quickstart.github/workflows/github-actions-demo.ymlname:GitHubActionsDemorun-name:${{github.actor}}istestingoutGitHubActions......
  • ant-design-vue日历面板 a-calendar 属性自定义设置
    通过自定义属性设置,实现一个周末与工作日的不同颜色设置效果图: 使用的属性:自定义头部设置headerRender自定义日期显示dateFullCellRender代码:<template><divclass="box"><h3>1.自定义头部;2.自定义日期显示,工作日显示,周末显示</h3><a-c......
  • 小程序自定义组件 - 数据方法与属性
    这块在组件中的定义和使用,同vue是大致相同的.在小程序组件中定义在.js的Component()中即可.data和methods小程序中,组件数据要定义在data中,而事件处理函数和自定义方法都定义在methods中.以一个页面点击+1的例子作为演示:(还是之前的cj组件)组件......
  • 使用注解开发
    在Spring4之后,要使用注解开发必须保证aop的包导入了使用注解需要导入context约束,增加注解的支持属性注入@ComponentpublicclassUser{//@Value("alice")publicStringname;@Value("alice")publicvoidsetName(Stringname){this.name......
  • 自定义序列化器类
    @Serialization是一个自定义装饰器,通常用于序列化Python对象。使用@Serialization装饰器可以将一个类转换为可序列化的对象,这样就可以将其存储到文件或通过网络传输。下面是一个使用@Serialization装饰器的示例:importjsondefSerialization(cls):defserializ......
  • 小程序自定义组件 - 创建与引用
    简单理解组件即"页面的一部分".组件化开发也更多是为了代码复用,方便管理和提高开发效率.前端的组件化开发我想大抵也是借鉴后端开发思想吧.从前端的实现来看,以vue为例即通过扩展自定义HTML标签的的形式,让其局部拥有"单文件"的功能(包括了模板,样式,逻辑).然后组......
  • Jmeter参数化的方式-用户参数、用户自定义变量、CSV文件设置、函数助手
    一、jmeter的主要功能参数化:事先准备好数据,脚本执行时从准备好的数据中取值;可将脚本中的某些输入使用参数来代替,在脚本运行时指定参数的取值范围和规则;在脚本运行时,根据需要选取不同的参数值作为输入,该方式成为数据驱动测试(DataDrivernTest,DDT),参数的取值范围称为数据池(Data......
  • .net core 自定义规范响应的中间件
    在本文中,我们将介绍如何使用.NETCore中的中间件来自定义规范响应,以便在API调用时返回统一的格式和错误信息。中间件是一种可以在请求和响应管道中执行逻辑的软件组件,它可以对请求或响应进行修改、拦截或处理。我们将使用一个简单的示例来演示如何创建和使用自定义规范响应的......
  • 自定义线程池详解
    自定义线程池ThreadPoolExecutorexecutor=newThreadPoolExecutor(5,10,200,TimeUnit.MILLISECONDS, newArrayBlockingQueue<Runnable>(5));第一个参数:核心线程池大小,默认创建后就不会销毁,需要设置allowCoreThreadTimeOut为true时会销毁第二个参数:线程池最大大......
  • 内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
    原文:https://blog.csdn.net/yyzsyx/article/details/129576582文章目录一、篇头二、内核部分2.1源码下载2.1.1官网2.1.2镜像站点2.1.3代码下载2.2编译2.2.1设置工具链2.2.2配置2.2.3make2.2.4编译成功三、busybox部分3.1源码下载3.2编译3.2.1配置3.2.3编译3.2.4查......