spring+mybatis基於 AOP實現業務日誌管理

最近在項目上用到了操做日誌的相關,以前的解決方案就是本身寫一個日誌project,而後統一調用日誌接口便可,這樣方便自定義定製,由於有不少設備控制之類的都是須要確認一下的,可是,對數據的操做,好比,增刪改查還用這個,就有點多餘了,感受太麻煩了,因此,想一想有沒有日誌管理這樣的框架能夠調用,而後就想到了spring的AOP。html

整個項目的結構以下java

第一步,定義日誌類web

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,因此,這裏上面的操做類繼承了其主鍵類,spring

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和日誌的Servicesql

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,因此,先自定義一個註解數據庫

第三步,定義一個註解apache

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();
}
自定義註解

須要瞭解更多註解,請點擊更多註解mybatis

第四步,重頭戲來了,真正的Aspectmvc

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

我這裏使用了一個時間格式類,以下app

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認證框架時保存的參數)

相關文章
相關標籤/搜索