文档章节

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

gaomq
 gaomq
发布于 2017/07/25 16:41
字数 961
阅读 35
收藏 0
点赞 0
评论 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
粉丝 2
博文 53
码字总数 22928
作品 0
沈阳
程序员
springboot + shiro 权限注解、请求乱码解决、统一异常处理

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

wyait ⋅ 06/06 ⋅ 0

SpringBoot 2.0 系列006 --启动实战之注解使用

SpringBoot 2.0 系列006 --启动实战之注解使用 一些误区说明 网上很多教程默认使用SpringBootApplcation注解,且只用这个即可扫描启动类包下所有的bean。 而官方默认教程使用的是@EnableAuto...

路上有你0314 ⋅ 05/18 ⋅ 0

恒宇少年/spring-boot-chapter

简书整套文档以及源码解析 专题 专题名称 专题描述 001 Spring Boot 核心技术 讲解SpringBoot一些企业级层面的核心组件 002 Spring Cloud 核心技术 对Spring Cloud核心技术全面讲解 003 Quer...

恒宇少年 ⋅ 04/19 ⋅ 0

SpringBoot 入门实例

SpringBoot 是由 pivotal 提供的 Java 开发框架,伴随着 spring 4.0 版本一起发布,旨在简化 Spring 项目的初始化及开发过程,框架本身为开发过程提供了大量的默认配置,当然我们也可以通过简...

Aotian ⋅ 05/29 ⋅ 0

SpringBoot 2.0 系列005 --启动实战之SpringApplication应用

SpringBoot 2.0 系列005 --启动实战之SpringApplication应用 2.X 官方示例 注意是只使用了@EnableAutoConfiguration,默认是只扫描这一个我们自定义的类到bean中。不含子包和本包。下一篇我们...

路上有你0314 ⋅ 05/17 ⋅ 0

打造个人版微信小程序(1)——本地开发api接口调用

如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢。   从今天开始,开始打造一个个人版的微信小程序,尽早上线,方便大家使用以及技术讨论。这套小程序...

u013088062 ⋅ 05/01 ⋅ 0

Spring Boot的@Service和@Autowired和@ComponentScan注解

Spring Boot的@Service和@Autowired和@ComponentScan注解 SpringBoot项目的Bean装配默认规则是根据Application类(SpringBoot项目入口类)所在的包位置从上往下扫描,即只扫描该类所在的包及...

蓝色老鼠 ⋅ 06/01 ⋅ 0

springboot整合kaptcha验证码

前言: 关于kaptcha简介以及spring整合kaptcha,我在另一篇文章中已详细讲解,请参考:spring整合kaptcha验证码。 本文将介绍springboot整合kaptcha的两种方式。 开发工具及技术: 1、idea 2017...

贪挽懒月 ⋅ 05/10 ⋅ 0

最新官方版的SpringBoot 整合 Dubbo

上次的那个springboot和dubbo的整合版本中,dubbo的版本是2.5.3,它的Service注解和事务不能同时使用,会造成扫描失效,2.6.2的dubbo版本已经纠正了此不便,官方也给出了与springboot整合的q...

FantJ ⋅ 06/09 ⋅ 0

Spring Boot干货系列: (三)启动原理解析

     前言   前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次博主就跟你们一起一步步...

后端编程嘟 ⋅ 2017/03/13 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 51分钟前 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 7

mysql in action / alter table

change character set ALTER SCHEMA `employees` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci ;ALTER TABLE `employees`.`t2` CHARACTER SET = utf8mb4 , COLLAT......

qwfys ⋅ 今天 ⋅ 0

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

Linux下php访问远程ms sqlserver

1、安装freetds(略,安装在/opt/local/freetds 下) 2、cd /path/to/php-5.6.36/ 进入PHP源码目录 3、cd ext/mssql进入MSSQL模块源码目录 4、/opt/php/bin/phpize生成编译配置文件 5、 . ./...

wangxuwei ⋅ 昨天 ⋅ 0

如何成为技术专家

文章来源于 -- 时间的朋友 拥有良好的心态。首先要有空杯心态,用欣赏的眼光发现并学习别人的长处,包括但不限于工具的使用,工作方法,解决问题以及规划未来的能力等。向别人学习的同时要注...

长安一梦 ⋅ 昨天 ⋅ 0

Linux vmstat命令实战详解

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令...

刘祖鹏 ⋅ 昨天 ⋅ 0

MySQL

查看表相关命令 - 查看表结构    desc 表名- 查看生成表的SQL    show create table 表名- 查看索引    show index from  表名 使用索引和不使用索引 由于索引是专门用于加...

stars永恒 ⋅ 昨天 ⋅ 0

easyui学习笔记

EasyUI常用控件禁用方法 combobox $("#id").combobox({ disabled: true }); ----- $("#id").combobox({ disabled: false}); validatebox $("#id").attr("readonly", true); ----- $("#id").r......

miaojiangmin ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部