文档章节

springboot使用注解的方式记录用户业务操作

gaomq
 gaomq
发布于 2017/07/25 16:41
字数 961
阅读 56
收藏 0

因为我们是使用注解的方式记录日志,所以我们首先要定义一个注解。

1.定义注解

   

/**
 * 标记需要做业务日志的方法
 *
 * @author gao.mq
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface BussinessLog {

    /**
     * 业务的名称,例如:"修改用户"
     */
    String value() default "";

    /**
     * 被修改的实体的唯一标识,例如:用户实体的唯一标识为"id"
     */
    String key() default "id";

    /**
     * 字典(用于查找key的中文名称和字段的中文名称)
     */
    String dict() default "SystemDict";
}

2.定义aop进行拦截

/**
 * 日志记录
 *
 * @author gao.mq
 */
@Aspect
@Component
public class LogAop {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut(value = "@annotation(com.common.annotion.log.BussinessLog)")
    public void cutService() {
    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {

        //先执行业务
        Object result = point.proceed();

        try {
            handle(point);
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        }

        return result;
    }

    private void handle(ProceedingJoinPoint point) throws Exception {

        //获取拦截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();

        //如果当前用户未登录,不做日志
        ShiroUser user = ShiroKit.getUser();
        if (null == user) {
            return;
        }

        //获取拦截方法的参数
        String className = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();

        //获取操作名称
        BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
        String bussinessName = annotation.value();
        String key = annotation.key();
        String dictClass = annotation.dict();

        StringBuilder sb = new StringBuilder();
        for (Object param : params) {
            sb.append(param);
            sb.append(" & ");
        }

        //如果涉及到修改,比对变化
        String msg;
        if (bussinessName.indexOf("修改") != -1 || bussinessName.indexOf("编辑") != -1) {
            Object obj1 = LogObjectHolder.me().get();
            Map<String, String> obj2 = HttpKit.getRequestParameters();
            msg = Contrast.contrastObj(dictClass, key, obj1, obj2);
        } else {
            Map<String, String> parameters = HttpKit.getRequestParameters();
            AbstractDictMap dictMap = DictMapFactory.createDictMap(dictClass);
            msg = Contrast.parseMutiKey(dictMap,key,parameters);
        }
        LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg));
    }
}

上面的代码有身份认证个人Id等方面的要求,可以根据自己的需求进行增删。都要注释

3. 上面最后一行向数据库执行插入日志记录执行的信息。看下LogManager

/**
 * 日志管理器
 *  开启线程进行异步操作
 * @author gao.mq
 */
public class LogManager {

    //日志记录操作延时
    private final int OPERATE_DELAY_TIME = 10;

    //异步操作记录日志的线程池
    private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);

    private LogManager() {
    }

    public static LogManager logManager = new LogManager();

    public static LogManager me() {
        return logManager;
    }

    public void executeLog(TimerTask task) {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }
}

4.executeLog()方法的参数是TimerTask,我们操作的时候传入的是LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg)

/**
 * 日志操作任务创建工厂
 *
 * @author gao.mq
 */
public class LogTaskFactory {

    private static Logger logger = LoggerFactory.getLogger(LogManager.class);
    private static OperationLogMapper operationLogMapper = Db.getMapper(OperationLogMapper.class);

    public static TimerTask bussinessLog(final Integer userId, final String bussinessName, final String clazzName, final String methodName, final String msg) {
        return new TimerTask() {
            @Override
            public void run() {
                OperationLog operationLog = LogFactory.createOperationLog(
                        LogType.BUSSINESS, userId, bussinessName, clazzName, methodName, msg, LogSucceed.SUCCESS);
                try {
                    //这里进行向日志表插入数据
                    operationLogMapper.insert(operationLog);
                } catch (Exception e) {
                    logger.error("创建业务日志异常!", e);
                }
            }
        };
    }

    public static TimerTask exceptionLog(final Integer userId, final Exception exception) {
        return new TimerTask() {
            @Override
            public void run() {
                String msg = ToolUtil.getExceptionMsg(exception);
                OperationLog operationLog = LogFactory.createOperationLog(
                        LogType.EXCEPTION, userId, "", null, null, msg, LogSucceed.FAIL);
                try {
                     //这里进行向日志表插入数据
                    operationLogMapper.insert(operationLog);
                } catch (Exception e) {
                    logger.error("创建异常日志异常!", e);
                }
            }
        };
    }
}

在记录数据变更的时候,对比变化临时保存修改的变量。不一定需要记录,这里一并贴出来。

/**
 * 被修改的对象临时存放的地方
 * @author gao.mq
 */
@Component
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)
public class LogObjectHolder implements Serializable{

    private Object object = null;

    public void set(Object obj) {
        this.object = obj;
    }

    public Object get() {
        return object;
    }

    public static LogObjectHolder me(){
        LogObjectHolder bean = SpringContextHolder.getBean(LogObjectHolder.class);
        return bean;
    }
}

记录和比较对象属性的变化

/**
 * 字典映射抽象类
 * @author gao.mq
 */
public abstract class AbstractDictMap {

    protected HashMap<String, String> dictory = new HashMap<>();
    protected HashMap<String, String> fieldWarpperDictory = new HashMap<>();

    public AbstractDictMap(){
        put("id","主键id");
        init();
        initBeWrapped();
    }

    /**
     * 初始化字段英文名称和中文名称对应的字典
     */
    public abstract void init();

    public String get(String key) {
        return this.dictory.get(key);
    }

    public void put(String key, String value) {
        this.dictory.put(key, value);
    }

    public String getFieldWarpperMethodName(String key){
        return this.fieldWarpperDictory.get(key);
    }

    public void putFieldWrapperMethodName(String key,String methodName){
        this.fieldWarpperDictory.put(key,methodName);
    }
}

 

使用:

@RequestMapping("/add")
@ResponseBody
@BussinessLog(value = "新增订单",key="contractNumber",dict = Dict.OrderMap)
public Tip addOrder(Order order){
    return null;
}

 

© 著作权归作者所有

共有 人打赏支持
gaomq
粉丝 3
博文 59
码字总数 23993
作品 0
合肥
程序员
私信 提问
SpringBoot 学习二:操作数据库

本文将从以下几个方面介绍: 前言 配置数据源 SpringBoot 整合 Mybatis SpringBoot 整合 JdbcTemplate SpringBoot 整合 Redis 前言 在上篇文章 SpringBoot 学习一 中已经学习了 SpringBoot的...

tsmyk0715
09/26
0
0
基于SpringBoot,更简洁的后台管理系统 - Guns

Guns 新版Guns基于SpringBoot全面升级,完美整合springmvc + shiro + mybatis-plus + beetl! 在不用写xml配置(V1.0)的基础上进一步简化项目配置,让您更专注于业务开发!抛弃传统spring xml的配...

stylefeng
2017/05/18
0
83
spring boot整合Websocket笔记

特别说明:自学笔记 使用websocket有两种方式: 使用sockjs, 使用h5的标准。 使用Html5标准自然更方便简单,所以记录的是配合h5的使用方法。 1、pom.xml中添加如下: 核心是@ServerEndpoint...

jackcooper2015
2017/12/28
0
0
SpringBoot学习之基础篇

在前面的博文中,已经演示过springboot与Mybatis集成的实例,本篇再来探讨一下SpringBoot的基础。 一。关于SpringBoot   SpringBoot可以基于Spring轻松创建可以“运行”的、独立的、生产级...

java~nick
2017/10/31
0
0
springboot + shiro 权限注解、请求乱码解决、统一异常处理

springboot + shiro 权限注解、请求乱码解决、统一异常处理 前篇 后台权限管理系统 相关: spring boot + mybatis + layui + shiro后台权限管理系统 springboot + shiro之登录人数限制、登录...

wyait
06/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

numpy常用操作

水平合并数组 import numpy as npa = [1,2,3]b = [4,5,6]np.hstack((a,b))# array([1, 2, 3, 4, 5, 6])c = [a,['a','b','c']]d = [b,['d','e','f']]np.hstack((c,d))#array([['1'......

datadev_sh
13分钟前
0
0
四种检测异常值的常用技术简述

摘要: 本文介绍了异常值检测的常见四种方法,分别为Numeric Outlier、Z-Score、DBSCAN以及Isolation Forest 在训练机器学习算法或应用统计技术时,错误值或异常值可能是一个严重的问题,它们...

阿里云官方博客
16分钟前
0
0
如何删除本地服务

Microsoft Windows [版本 10.0.17134.407] (c) 2018 Microsoft Corporation。保留所有权利。 C:\WINDOWS\system32>SC 描述: SC 是用来与服务控制管理器和服务进行通信 的命令行程序。 用法:...

码农屌丝
28分钟前
1
0
Web安全学习规划

一名合格的Web安全工程师是要具备很多的知识点,不但要对网站架构熟悉,通讯协议,测试流程与测试工具使用,漏洞利用脚本编写,还有需要经验的积累等。 互联网进入下半场,竞争越发的激烈,能...

Linux就该这么学
33分钟前
1
0
爬虫Requests基本使用

Requests基本使用 安装 pip install requests 一、Requests模块请求 获取网页(不带参数) r = requests.get('http://www.chinahufei.com')r = requests.post('http://www.chinahufei.com')......

chinahufei
33分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部