SpringAOP+注解实现简单的日志管理

  

  今天在再次深入学习SpringAOP之后想着基于注解的AOP实现日志功能,在面试过程中我们也经常会被问到:假如项目已经上线,如何增加一套日志功能?我们会说使用AOP,AOP也符合开闭原则:对代码的修改禁止的,对代码的扩展是允许的。今天经过自己的实践简单的实现了AOP日志。

  在这里我只是简单的记录下当前操作的人、做了什么操作、操作结果是正常还是失败、操作时间,实际项目中,如果我们需要记录的更详细,可以记录当前操作人的详细信息,比如说部门、身份证号等信息,这些信息可以直接从session中获取,也可以从session中获取用户ID之后调用userService从数据库获取。我们还可以记录用户调用了哪个类的哪个方法,我们可以使用JoinPoint参数获取或者利用环绕通知ProceedingJoinPoint去获取。可以精确的定位到类、方法、参数,如果有必要我们就可以记录在日志中,看业务需求和我们的日志表的设计。

  实现的大致思路是:

    1.前期准备,设计日志表和日志类,编写日志Dao和Service以及实现

    2.自定义注解,注解中加入几个属性,属性可以标识操作的类型(方法是做什么的)

    3.编写切面,切点表达式使用上面的注解直接定位到使用注解的方法,

    4.编写通知,通过定位到方法,获取上面的注解以及注解的属性,然后从session中直接获取或者从数据库获取当前登录用户的信息,最后根据业务处理一些日志信息之后调用日志Service存储日志。

  

  其实日志记录可以针对Controller层进行切入,也可以选择Service层进行切入,我选择的是基于Service层进行日志记录。网上的日志记录由的用前置通知,有的用环绕通知,我选择在环绕通知中完成,环绕通知中可以完成前置、后置、最终、异常通知的所有功能,因此我选择了环绕通知。(关于AOP的通知使用方法以及XML、注解AOP使用方法参考;http://www.cnblogs.com/qlqwjy/p/8729280.html)

    

下面是具体实现:

1.日志数据库:

CREATE TABLE `logtable` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `operateor` varchar(5) DEFAULT NULL,
  `operateType` varchar(20) DEFAULT NULL,
  `operateDate` datetime DEFAULT NULL,
  `operateResult` varchar(4) DEFAULT NULL,
  `remark` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

  简单的记录操作了操作人,操作的类型,操作的日期,操作的结果。如果想详细的记录,可以将操作的类名与操作的方法名以及参数信息也新进日志,在环绕通知中利用反射原理即可获取这些参数(参考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/8729280.html)。

2.日志实体类:

Logtable.java

package cn.xm.exam.bean.log;

import java.util.Date;

public class Logtable {
    private Integer id;

    private String operateor;

    private String operatetype;

    private Date operatedate;

    private String operateresult;

    private String remark;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOperateor() {
        return operateor;
    }

    public void setOperateor(String operateor) {
        this.operateor = operateor == null ? null : operateor.trim();
    }

    public String getOperatetype() {
        return operatetype;
    }

    public void setOperatetype(String operatetype) {
        this.operatetype = operatetype == null ? null : operatetype.trim();
    }

    public Date getOperatedate() {
        return operatedate;
    }

    public void setOperatedate(Date operatedate) {
        this.operatedate = operatedate;
    }

    public String getOperateresult() {
        return operateresult;
    }

    public void setOperateresult(String operateresult) {
        this.operateresult = operateresult == null ? null : operateresult.trim();
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark == null ? null : remark.trim();
    }
}

3.日志的Dao层使用的是Mybatis的逆向工程导出的mapper,在这里就不贴出来了

4.日志的Service层和实现类

  • LogtableService.java接口
package cn.xm.exam.service.log;

import java.sql.SQLException;

import cn.xm.exam.bean.log.Logtable;

/**
 * 日志Service
 *
 * @author liqiang
 *
 */
public interface LogtableService {
    /**
     * 增加日志
     * @param log
     * @return
     * @throws SQLException
     */
    public boolean addLog(Logtable log) throws SQLException;
}
  • LogtableServiceImpl实现类
package cn.xm.exam.service.impl.log;

import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.xm.exam.bean.log.Logtable;
import cn.xm.exam.mapper.log.LogtableMapper;
import cn.xm.exam.service.log.LogtableService;

@Service
public class LogtableServiceImpl implements LogtableService {
    @Autowired
    private LogtableMapper logtableMapper;
    @Override
    public boolean addLog(Logtable log) throws SQLException {
        return logtableMapper.insert(log) > 0 ? true : false;
    }

}

5.自定义注解:

package cn.xm.exam.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 日志注解
 *
 * @author liqiang
 *
 */
@Target(ElementType.METHOD) // 方法注解
@Retention(RetentionPolicy.RUNTIME) // 运行时可见
public @interface LogAnno {
    String operateType();// 记录日志的操作类型
}

6.在需要日志记录的方法中使用注解:(此处将注解写在DictionaryServiceImpl方法上)

package cn.xm.exam.service.impl.common;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import cn.xm.exam.annotation.LogAnno;
import cn.xm.exam.bean.common.Dictionary;
import cn.xm.exam.bean.common.DictionaryExample;
import cn.xm.exam.mapper.common.DictionaryMapper;
import cn.xm.exam.mapper.common.custom.DictionaryCustomMapper;
import cn.xm.exam.service.common.DictionaryService;

/**
 * 字典表的实现类
 *
 * @author
 *
 */
@Service
public class DictionaryServiceImpl implements DictionaryService {

    @Resource
    private DictionaryMapper dictionaryMapper;/**
     * 1、添加字典信息
     */
    @LogAnno(operateType = "添加了一个字典项")
    @Override
    public boolean addDictionary(Dictionary dictionary) throws Exception {
        int result = dictionaryMapper.insert(dictionary);
        if (result > 0) {
            return true;
        } else {
            return false;
        }
    }
}

7.编写通知,切入到切点形成切面(注解AOP实现,环绕通知记录日志。)

  注意:此处是注解AOP,因此在spring配置文件中开启注解AOP

    <!-- 1.开启注解AOP -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

LogAopAspect.java

package cn.xm.exam.aop;

import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.Date;

import org.apache.struts2.ServletActionContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import cn.xm.exam.annotation.LogAnno;
import cn.xm.exam.bean.log.Logtable;
import cn.xm.exam.bean.system.User;
import cn.xm.exam.service.log.LogtableService;

/**
 * AOP实现日志
 *
 * @author liqiang
 *
 */
@Component
@Aspect
public class LogAopAspect {

    @Autowired
    private LogtableService logtableService;// 日志Service
    /**
     * 环绕通知记录日志通过注解匹配到需要增加日志功能的方法
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cn.xm.exam.annotation.LogAnno)")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        // 1.方法执行前的处理,相当于前置通知
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上面的注解
        LogAnno logAnno = method.getAnnotation(LogAnno.class);
        // 获取操作描述的属性值
        String operateType = logAnno.operateType();
        // 创建一个日志对象(准备记录日志)
        Logtable logtable = new Logtable();
        logtable.setOperatetype(operateType);// 操作说明

        // 整合了Struts,所有用这种方式获取session中属性(亲测有效)
         User user = (User) ServletActionContext.getRequest().getSession().getAttribute("userinfo");//获取session中的user对象进而获取操作人名字
        logtable.setOperateor(user.getUsername());// 设置操作人

        Object result = null;
        try {
            //让代理方法执行
            result = pjp.proceed();
            // 2.相当于后置通知(方法成功执行之后走这里)
            logtable.setOperateresult("正常");// 设置操作结果
        } catch (SQLException e) {
            // 3.相当于异常通知部分
            logtable.setOperateresult("失败");// 设置操作结果
            throw e;
        } finally {
            // 4.相当于最终通知
            logtable.setOperatedate(new Date());// 设置操作日期
            logtableService.addLog(logtable);// 添加日志记录
        }
        return result;
    }
}

  通过拦截带有 cn.xm.exam.annotation.LogAnno 注解的方法,根据参数获取到方法,然后获取方法的LogAnno注解,获取注解的属性,在方法执行前后对其进行处理,实现AOP功能。

8.测试:

  在页面上添加一个字典之后打断点进行查看:

  • 会话中当前登录的用户信息:

  • 当前日志实体类的信息

  • 查看数据库:
mysql> select * from logtable\G
*************************** 1. row ***************************
           id: 1
    operateor: 超级管理员
  operateType: 添加了一个字典项
  operateDate: 2018-04-08 20:46:19
operateResult: 正常
       remark: NULL

   到这里基于注解AOP+注解实现日志记录基本实现了。

最后给几个链接,不明白上面的可以参考:

  注解的使用:http://www.cnblogs.com/qlqwjy/p/7139068.html

  Spring中获取request和session对象:http://www.cnblogs.com/qlqwjy/p/8747136.html

  SpringAOP的使用方法:http://www.cnblogs.com/qlqwjy/p/8729280.html

原文地址:https://www.cnblogs.com/qlqwjy/p/8747476.html

时间: 2024-10-13 22:25:56

SpringAOP+注解实现简单的日志管理的相关文章

SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)

首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题.这个时候就马上打开CRT或者SSH连上服务器拿日子来分析.受网络的各种限制.于是我们就想为什么不能直接在管理后台查看报错的信息呢.于是日志管理就出现了. 其次个人觉得做日志管理最好的是Aop,有的人也喜欢用拦截器.都可以,在此我重点介绍我的实现方式. Aop有的人说拦截不到Controller.有的人说想拦AnnotationMethodHandlerAdapter截到Controller必须得拦截org.sprin

简单的日志管理代码

自己写的记录日志,定期删除日志的方法. 方法比较简单,记录一下吧. /// <summary> /// 写日志 /// </summary> /// <param name="strMsg">内容</param> /// <param name="strPath">路径(相对hycom下的文件夹路径)</param> /// <param name="fileName"

SpringAop进行日志管理。

在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个方法的调用.然后进行日志记录.使用过滤器的好处是可以自己选择性的对某一些方法进行过滤,记录日志.但是实现起来有点麻烦. 另外一种就是使用Spring的AOP了.这种方式实现起来非常简单,只要配置一下配置文件就可以了.可是这种方式会拦截下所有的对action的每个操作.使得效率比较低.不过想做详细日志

spring AOP自定义注解方式实现日志管理

转:spring AOP自定义注解方式实现日志管理 今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven />     <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->     <conte

SQL Server中的事务日志管理(4/9):简单恢复模式里的日志管理

当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会告诉你每个DBA应该知道的具体细节. 这个标题近乎是用词不当,因为很大程度上,运行在简单模式里不需要日志管理.在简单模式里,事务日志的唯一目的是在数据库恢复操作期间,保证事务的ACID属性,还有强制数据库的一致性和事务的持久性.事务日志不能被备份,不能用来数据库恢复,也不能用作日志传输. 在简单模式

Log4j日志管理的简单实例

大型项目中很多情况下要分析程序的日志信息,如何管理自己的日志信息至关重要.在应用程序中添加日志记录总的来说基于三个目的 , 监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作: 跟踪代码运行时轨迹,作为日后审计的依据: 担当集成开发环境中的调试器的作用,向文件或控制台打印代码的调试信息. 最普通的做法就是在代码中嵌入许多的打印语句,这些打印语句可以输出到控制台或文件中,比较好的做法就是构造一个日志操作类 来封装此类操作,而不是让一系列的打印语句充斥了代码的主体. 这篇文章主

LogCook 一个简单实用的Android日志管理工具

众所周知,日志的管理是软件系统很重要的一部分,千万不可忽略其重要性.完整的日志将会在系统维护中起着异常重要的作用,就好像磨刀不误砍柴工一样,日志就像对系统进行分析的工具,工具便捷了,对系统分析起来就能达到事半功倍的效果.开发者必须要明白日志的价值和意义,万万不可忽略和轻视. LogCook是一款非常简洁实用的Android日记管理工具.LogCook的中文翻译是日志厨师,你可以把它看作是一个日志美食家. 特点 作为一款日志管理工具它最大的特点就是简单实用,与Android原生的日志功能相比较它具

spring AOP自定义注解 实现日志管理

今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven />     <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->     <context:component-scan base-pac

SpringAop日志管理

Spring AOP    1.对AOP的理解 --  分工来做各个部分,运行时候整合的思想 2.理解 面向过程,面向对象,面向切面 的思想 1)面向过程:房间装修时,准备装一个灯,就拉一根电线,连接灯. 2)面向对象:设计房间中哪些位置需要使用电线接口,然后在相应的位置设置电线接口,以备以后使用. 3)面向切面:装修房子,先设计需要在哪些地方装上电线接口,就将电线接口先设置好并且不打开接口,此处即为连接点,当此处电线切口确实需要使用时将接口打开插电器即为切入点. 方面:功能(登陆 日志) 目标