zoukankan      html  css  js  c++  java
  • spring+mybatis基于 AOP实现业务日志管理

    最近在项目上用到了操作日志的相关,之前的解决方案就是自己写一个日志project,然后统一调用日志接口即可,这样方便自定义定制,因为有很多设备控制之类的都是需要确认一下的,但是,对数据的操作,比如,增删改查还用这个,就有点多余了,感觉太麻烦了,所以,想想有没有日志管理这样的框架可以调用,然后就想到了spring的AOP。

    整个项目的结构如下

    第一步,定义日志类

    package com.unisits.zngkpt.framework.logframe.pojo;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class LogOperation extends LogOperationKey {
        private String operatorId;
    
        private String comment;
    
        private String operatorResult;
    
        private Integer transTag;
    
        public String getOperatorId() {
            return operatorId;
        }
    
        public void setOperatorId(String operatorId) {
            this.operatorId = operatorId;
        }
    
        public String getComment() {
            return comment;
        }
    
        public void setComment(String comment) {
            this.comment = comment;
        }
    
        public String getOperatorResult() {
            return operatorResult;
        }
    
        public void setOperatorResult(String operatorResult) {
            this.operatorResult = operatorResult;
        }
    
        public Integer getTransTag() {
            return transTag;
        }
    
        public void setTransTag(Integer transTag) {
            this.transTag = transTag;
        }
    }
    操作类

    因为用mybatis的generate来生成model,所以,这里上面的操作类继承了其主键类,

    package com.unisits.zngkpt.framework.logframe.pojo;
    
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    @Component
    public class LogOperationKey {
        private Integer unitId;
    
        private Integer deviceId;
    
        private Integer deviceType;
    
        private Date endTime;
    
        public Integer getUnitId() {
            return unitId;
        }
    
        public void setUnitId(Integer unitId) {
            this.unitId = unitId;
        }
    
        public Integer getDeviceId() {
            return deviceId;
        }
    
        public void setDeviceId(Integer deviceId) {
            this.deviceId = deviceId;
        }
    
        public Integer getDeviceType() {
            return deviceType;
        }
    
        public void setDeviceType(Integer deviceType) {
            this.deviceType = deviceType;
        }
    
        public Date getEndTime() {
            return endTime;
        }
    
        public void setEndTime(Date endTime) {
            this.endTime = endTime;
        }
    }
    主键类

    第二步,定义日志的DAO和日志的Service

    package com.unisits.zngkpt.framework.logframe.mapper;
    
    import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
    import org.springframework.stereotype.Repository;
    
    /**
     * @author:lyy
     * @Date: 2017/6/25 10:33
     * @version:
     * @Description:
     */
    @Repository("logOperationDao")
    //@Component
    public interface LogOperationDao {
        /**
         * @author: lyy
         * @Time: 2017/6/25 10:35
         * @Descrption: 插入操作类信息
         * @param logOperation
         * @return
         * @throws
         */
        public int insertLogOperation(LogOperation logOperation);
    }
    日志DAO接口
    <?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.unisits.zngkpt.framework.logframe.mapper.LogOperationDao" >
      <resultMap id="BaseResultMap" type="com.unisits.zngkpt.framework.logframe.pojo.LogOperation" >
        <id column="unit_id" property="unitId" jdbcType="INTEGER" />
        <id column="device_id" property="deviceId" jdbcType="INTEGER" />
        <id column="device_type" property="deviceType" jdbcType="INTEGER" />
        <id column="end_time" property="endTime" jdbcType="TIMESTAMP" />
        <result column="operator_id" property="operatorId" jdbcType="VARCHAR" />
        <result column="comment" property="comment" jdbcType="VARCHAR" />
        <result column="operator_result" property="operatorResult" jdbcType="VARCHAR" />
        <result column="trans_tag" property="transTag" jdbcType="INTEGER" />
      </resultMap>
      <sql id="Base_Column_List" >
        unit_id, device_id, device_type, end_time, operator_id, comment, operator_result, 
        trans_tag
      </sql>
      <insert id="insertLogOperation" parameterType="com.unisits.zngkpt.framework.logframe.pojo.LogOperation" >
        insert into log_operation (unit_id, device_id, device_type, 
          end_time, operator_id, comment, 
          operator_result, trans_tag)
        values (#{unitId,jdbcType=INTEGER}, #{deviceId,jdbcType=INTEGER}, #{deviceType,jdbcType=INTEGER}, 
          #{endTime,jdbcType=TIMESTAMP}, #{operatorId,jdbcType=VARCHAR}, #{comment,jdbcType=VARCHAR}, 
          #{operatorResult,jdbcType=VARCHAR}, #{transTag,jdbcType=INTEGER})
      </insert>
    </mapper>
    日志DAO对应的mapper文件
    package com.unisits.zngkpt.framework.logframe.service;
    
    import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
    
    /**
     * @author:lyy
     * @Date: 2017/6/25 12:03
     * @version:
     * @Description:
     */
    public interface LogOperationService {
        /**
         * @author: lyy
         * @Time: 2017/6/25 12:05
         * @Descrption: 插入操作日志
         * @param logOperation
         * @return
         * @throws
         */
        public void InsertOperateLog(LogOperation logOperation);
    }
    日志Service接口
    package com.unisits.zngkpt.framework.logframe.service;
    
    import com.unisits.zngkpt.framework.logframe.mapper.LogOperationDao;
    import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * @author:lyy
     * @Date: 2017/6/25 12:03
     * @version:
     * @Description:
     */
    @Service("logOperationService")
    public class LogOperationServiceImpl implements LogOperationService{
        @Autowired
        LogOperationDao logOperationDao;
    
        /**
         * @param logOperation
         * @return
         * @throws
         * @author: lyy
         * @Time: 2017/6/25 12:05
         * @Descrption: 插入操作日志
         */
        @Override
        public void InsertOperateLog(LogOperation logOperation) {
            logOperationDao.insertLogOperation(logOperation);
        }
    }
    日志Service实现类

    上面的操作数据库的基本完成了,那么下面就开始真正的切面Aspect了,因为我们要实现注解来实现AOP,所以,先自定义一个注解

    第三步,定义一个注解

    package com.unisits.zngkpt.framework.logframe.bojo;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author:lyy
     * @Date: 2017/6/25 12:17
     * @version:
     * @Description:
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface OperAnnotation {
        //模块名
        String moduleName();
    
        //操作内容
        String option();
    }
    自定义注解

    需要了解更多注解,请点击更多注解

    第四步,重头戏来了,真正的Aspect

    package com.unisits.zngkpt.framework.logframe.bojo;
    
    import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
    import com.unisits.zngkpt.framework.logframe.service.LogOperationService;
    import com.unisits.zngkpt.framework.privilegeframe.bojo.ActiveUser;
    import org.apache.shiro.SecurityUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    
    import java.text.ParseException;
    
    /**
     * @author:lyy
     * @Date: 2017/6/25 12:07
     * @version:
     * @Description:
     */
    @Component
    @Aspect
    public class LogAspect {
        @Autowired
        private LogOperationService logOperationService;//日志记录Service
    
        /**
         * 方法切入点
         */
        @Pointcut("execution(* com.unisits.zngkpt.common.*.controller.InforeleaseController.*(..))")
        public void pointerCutMethod() {
        }
    
        /**
         * 后置通知
         *
         * @param jp
         *            连接点
         * @param annotation
         *            返回值
         */
        @AfterReturning(value = "pointerCutMethod() && @annotation(annotation)")
        public void doAfter(JoinPoint jp,  OperAnnotation annotation) throws ParseException{
    //        System.out.print(jp.getTarget().getClass() + "对象上被");
    //        System.out.print(jp.getSignature().getName() + "方法删除了");
            //在这里可以区分不同的操作,比如登录,退出,普通操作等等
            LogOperation log = new LogOperation();
            //通过注解获取当前属于那个模块
            log.setComment(annotation.moduleName()+":"+annotation.option());
            //获取操作时间
            log.setEndTime(DateUtils.getCurrentDate());
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            if (ra != null) {
                ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
                if (activeUser != null) {
                    log.setOperatorId(activeUser.getUserId());
                    log.setUnitId(Integer.valueOf(activeUser.getUnitId()));
                    log.setDeviceId(1);
                    log.setDeviceType(503);
                }
            }
            try {
                Object object = jp.getTarget();
                if (object != null) {
                    log.setOperatorResult("成功");
                }
                logOperationService.InsertOperateLog(log);
            } catch (Throwable e) {
                log.setOperatorResult("失败:" + e.getMessage());
                logOperationService.InsertOperateLog(log);
            }
        }
    
    }
    Aspect

    我这里使用了一个时间格式类,如下

    package com.unisits.zngkpt.framework.logframe.bojo;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;
    /**
     * @author:lyy
     * @Date: 2017/6/25 10:42
     * @version:
     * @Description:
     */
    @Component
    public class DateUtils {
        private static String defaultDatePattern = "yyyy-MM-dd HH:mm:ss.SSS";
    
        /**
         * 获得默认的 date pattern
         */
        public static String getDatePattern()
        {
            return defaultDatePattern;
        }
    
        /**
         * 返回预设Format的当前日期字符串
         */
        public static String getToday()
        {
            Date today = new Date();
            return format(today);
        }
    
        /**
         * 使用预设Format格式化Date成字符串
         */
        public static String format(Date date)
        {
            return date == null ? " " : format(date, getDatePattern());
        }
    
        /**
         * 使用参数Format格式化Date成字符串
         */
        public static String format(Date date, String pattern)
        {
            return date == null ? " " : new SimpleDateFormat(pattern).format(date);
        }
    
        /**
         * 使用预设格式将字符串转为Date
         */
        public static Date parse(String strDate) throws ParseException
        {
            return StringUtils.isBlank(strDate) ? null : parse(strDate,
                    getDatePattern());
        }
    
        /**
         * 使用参数Format将字符串转为Date
         */
        public static Date parse(String strDate, String pattern)
                throws ParseException
        {
            return StringUtils.isBlank(strDate) ? null : new SimpleDateFormat(
                    pattern).parse(strDate);
        }
    
        /**
         * 在日期上增加数个整月
         */
        public static Date addMonth(Date date, int n)
        {
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            cal.add(Calendar.MONTH, n);
            return cal.getTime();
        }
    
        public static String getLastDayOfMonth(String year, String month)
        {
            Calendar cal = Calendar.getInstance();
            //
            cal.set(Calendar.YEAR, Integer.parseInt(year));
            // 月,因为Calendar里的月是从0开始,所以要-1
            // cal.set(Calendar.MONTH, Integer.parseInt(month) - 1);
            // 日,设为一号
            cal.set(Calendar.DATE, 1);
            // 月份加一,得到下个月的一号
            cal.add(Calendar.MONTH, 1);
            // 下一个月减一为本月最后一天
            cal.add(Calendar.DATE, -1);
            return String.valueOf(cal.get(Calendar.DAY_OF_MONTH));// 获得月末是几号
        }
    
        public static Date getDate(String year, String month, String day)
                throws ParseException
        {
            String result = year + "- "
                    + (month.length() == 1 ? ("0 " + month) : month) + "- "
                    + (day.length() == 1 ? ("0 " + day) : day);
            return parse(result);
        }
    
        public static Date getCurrentDate()  throws ParseException{
            String strNow = new SimpleDateFormat(getDatePattern()).format(new Date());
            return new SimpleDateFormat(getDatePattern()).parse(strNow);
        }
    }
    DateUtils

    完成了,那么怎么告知spring容器我们有这个呢?那就是开启注解,该配置必须写在springmvc的配置文件,由于我们现在的方法切入点是在controller层,如果你定义在spring的配置文件里面,不会起作用。这牵扯到父子容器的问题。spring默认的主配置文件为父容器,springmvc为子容器,子容器可以使用父容器里面的配置信息,但是父容器却无法使用子容器的配置信息。

      <!-- 加入Aspectj配置 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>

    好的,现在注解就开启了,那么怎么在控制器里调用呢?

    如下

        @OperAnnotation(moduleName = "情报板页面",option = "打开")
        @RequestMapping("/infoman")
        public String QueryInfoRelease(){
            return CommonLib.INFORELEASE_URL_DIR+"inforelease";
        }

    好的,这里基于注解开发的完成了。

    更所详细的如下:

    1,自定义注解

    2,Aspect 切入点的匹配方式

    3,本日志源码和jar包(因为这里有ActiveUser,自定义的类,主要用于shiro认证框架时保存的参数)

  • 相关阅读:
    day06 tar命令使用,vim简单操作以及linux开机过程
    day05 创建用户过程、文件夹,文件等权限修改等
    简单算法
    day04
    day03
    Vim常用快捷键
    day02
    ssh注解开发
    spring07 JDBC
    spring06Aop
  • 原文地址:https://www.cnblogs.com/ningheshutong/p/7077959.html
Copyright © 2011-2022 走看看