1. 程式人生 > >自定義註解+Spring AOP實現記錄使用者操作日誌

自定義註解+Spring AOP實現記錄使用者操作日誌

一、背景

    專案中需要對使用者的各種操作做詳細的操作日誌記錄,需要記錄使用者操作的操作模組、具體操作以及操作的資料記錄ID等。     若寫一個方法去儲存操作,則需要每次手動去呼叫。由於是非業務性的操作,並且大量的重複操作,Spring AOP就能很好的解決這個問題。      由於使用者操作的實現方法並不在同一個類中,而且每個操作的說明也不一樣,所以用自定義註解作為切入點,來記錄使用者不同操作及其操作說明。

 二、配置

2.1、匯入jar包

  • spring-aop.jar
  • aspectjrt.jar
  • aspectjweaver.jar
  • aopalliance-1.0.jar

2.2、Spring的ApplicationContext.xml配置檔案

    2.2.1 配置標頭檔案

xmlns:aop="http://www.springframework.org/schema/aop"
<!-- 在xsi:schemaLocation中新增 -->
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"

注:若沒新增則會報 ==》     java錯誤-The prefix "aop" for element "aop:aspectj-autoproxy" is not bound.

    2.2.2 配置註解掃描和AOP自動代理

<!-- 配置元件掃描功能 -->
<context:component-scan base-package="com.test"/>
<!-- 配置自動代理功能 -->
<aop:aspectj-autoproxy />
<aop:config proxy-target-class="true"></aop:config>

三、建立自定義註解

package com.test.common.annotation;

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


/**
 * 品目操作日誌註解
 * @author zhoujin
 */
@Target(ElementType.METHOD)  
@Retention(RetentionPolicy.RUNTIME) 
public @interface OperationLogAnno {
	
	/** 模組 */
	String module();
	
	/** 具體操作 */
	String operate();
	
	/** 品目編號 */
	String pmbh();
	
	/** 備註說明 */
	String remarks() default "";

}

注:註解中定義的方法若沒有給預設值,則寫該註解的時候必須給該方法賦值!

四、Spring AOP切面類

package com.test.common.aop;

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

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.test.common.annotation.AnnotationResolver;
import com.test.common.annotation.OperationLogAnno;
import com.test.modules.sys.entity.User;
import com.test.modules.sys.utils.UserUtils;
import com.test.modules.zxztb.entity.operationLog.OperationLog;
import com.test.modules.zxztb.service.operationLog.OperationLogService;

/**
 * 品目操作日誌切面類
 * @author zhoujin
 * @date 2018-10-23
 */
@Aspect
@Component("operationLogAspect")
public class OperationLogAspect {
	
    private static final Logger log = LoggerFactory.getLogger(OperationLogAspect.class);
    
    @Autowired
    private OperationLogService operationLogService;
	
	// 配置織入點(以OperationLog註解為標誌)
    @Pointcut("@annotation(com.test.common.annotation.OperationLogAnno)")
    public void logPointCut() {
    }
 
    /**
     * 前置通知 用於攔截操作,在方法返回後執行
     * @param joinPoint 切點
     */
    @AfterReturning(pointcut = "logPointCut()")
    public void doBefore(JoinPoint joinPoint) {
    	handleLog(joinPoint, null);
    }
 
    /**
     * 攔截異常操作,有異常時執行
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e) {
    	handleLog(joinPoint, e);
    }
 
    private void handleLog(JoinPoint joinPoint, Exception e) {
	try {
	    // 獲得註解
		OperationLogAnno controllerLog = getAnnotationLog(joinPoint);
	    if (controllerLog == null) {
	    	return;
	    }
	    // 品目編號
	    Object pmbh = controllerLog.pmbh();
	    // 模組詳情
	    Object module = controllerLog.module();
	    // 具體操作
	    Object operate = controllerLog.operate();
	    // 備註說明
	    Object remarks = controllerLog.remarks();
	    // 操作使用者資訊
	    User currentUser = UserUtils.getUser();
	    // 訪問類名
	    String className = joinPoint.getTarget().getClass().getName();
	    // 訪問方法名
	    String methodName = joinPoint.getSignature().getName();
	    // 儲存日誌
	    log.info("\n====================================== 儲存操作日誌  ======================================");
	    OperationLog operationLog = new OperationLog();
	    operationLog.setPmbh(pmbh.toString());
	    operationLog.setModule(module.toString());
	    operationLog.setOperate(operate.toString());
	    operationLog.setOperate(remarks.toString());
	    operationLog.setOperatorLoginname(currentUser.getLoginName());
	    operationLog.setOperatorName(currentUser.getName());
	    operationLog.setOperateDate(new Date());
	    operationLog.setClassname(className);
	    operationLog.setMethodname(methodName);
	    
	    operationLogService.save(operationLog);
	    log.info("\n=====================================================================================\n");
	} catch (Exception exp) {
	    // 記錄本地異常日誌
	    log.error("\n====================================== 異常資訊通知 ======================================");
	    log.error("異常資訊:{}", exp.getMessage());
	    log.error("\n====================================================================================\n");
	    exp.printStackTrace();
	}
    }
 
    /**
     * 是否存在註解,如果存在就獲取
     */
    private static OperationLogAnno getAnnotationLog(JoinPoint joinPoint) throws Exception {
	Signature signature = joinPoint.getSignature();
	MethodSignature methodSignature = (MethodSignature) signature;
	Method method = methodSignature.getMethod();
	if (method != null) {
            return method.getAnnotation(OperationLogAnno.class);
	}
	return null;
    }
    
}

五、Controller上使用

@ResponseBody
@RequestMapping(value = "kaiQicb")
@RequiresPermissions("zxztb:dljgKbkzt:kqcb")
@OperationLogAnno(module="控制檯", operate="開啟流程", pmbh="1111111")
public AjaxJson kaiQicb(String id) {
    AjaxJson j = new AjaxJson();
    String message = "開啟流程成功";
    Zfcgpmbxm zfcgpmbxm = zfcgpmbxmService.get(id);
    try {
        zfcgpmbxm.setFlowstatus("7");
        zfcgpmbxmService.save(zfcgpmbxm);
    } catch (Exception e) {
        e.printStackTrace();
        j.setSuccess(false);
        message = "開啟流程失敗";
    }
    j.setMsg(message);
    return j;
}