Custom annotation + Spring AOP log records of user actions implemented

I. Background

    Projects user to do various operations detailed logging operation, the recording operation module requires a user operation, and the specific operation of the data recording and other operations ID.
    If the way to write a save operation, you need to call manually each time. Since the operation of the business of right and wrong, and a lot of repeat, Spring AOP can be a good solution to this problem.
     Since the implementation of the user operation is not in the same class, and description of each operation are not the same, so as a starting point with a custom annotations, and to record the user different operating instructions.

 Second, placement

2.1, introducing jar package

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

Spring AOP related jar package download

2.2, Spring's configuration file ApplicationContext.xml

    2.2.1 configuration header

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"

Note: if not add will be reported == "
    the Java error -The prefix" aop "for element" aop: aspectj-autoproxy "is not bound.


    2.2.2 Configuration Notes scans and automatic proxy AOP

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

Third, create a custom annotation

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 "";

}

Note: The annotated method defined default value if not given, then the time to write this comment must be assigned to this method!

Four, Spring AOP aspect class

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.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 doAfterReturn(JoinPoint joinPoint) {
    	handleLog(joinPoint, null);
    }
 
    /**
     * 拦截异常操作,有异常时执行
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrow(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;
    }
    
}

Fifth, use the 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;
}

Note: pmbh on the notes is written here dead, there is no effect. But the reality is, here pmbh should take the value of the front page came parameter id, then how can the annotation to the value of the parameter can get it?

java binding solution method parameters in the annotation


Sixth, download the above node2017 blog in AnnotationResolver, you can get front page came parameter values

6.1 Controller in

@OperationLogAnno(module="控制台", operate="开启流程", pmbh="#{id}")
public AjaxJson kaiQiKaiBiao(String id){
    ...
}
// 这样也可以
@OperationLogAnno(module="控制台", operate="开启流程", pmbh="#{pmxx.id}")
public AjaxJson kaiQiKaiBiao(Pmxx pmxx){
    ...
}

6.2 Spring AOP aspect class

 private void handleLog(JoinPoint joinPoint, Exception e) {
    try {
        // 获得注解
        OperationLogAnno controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog == null) {
            return;
        }
        AnnotationResolver annotationResolver = AnnotationResolver.newInstance();
        // 品目编号
        Object pmbh = annotationResolver.resolver(joinPoint, controllerLog.pmbh());
        // 模块详情
        Object module = annotationResolver.resolver(joinPoint, controllerLog.module());
        // 具体操作
        Object operate = annotationResolver.resolver(joinPoint, controllerLog.operate());
        // 备注说明
        Object remarks = annotationResolver.resolver(joinPoint, controllerLog.remarks());
        // 操作用户信息
        User currentUser = UserUtils.getUser();
        // 访问类名
        String className = joinPoint.getTarget().getClass().getName();
        // 访问方法名
        String methodName = joinPoint.getSignature().getName();
        // 保存日志
        ...

   } catch (Exception exp) {
        exp.printStackTrace();
}

6.4 AnnotationResolver Code (from: node2017 == "Java binding solution method parameters annotations )

package com.jeeplus.common.annotation;

import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

/**
 * 该类的作用可以把方法上的参数绑定到注解的变量中,注解的语法#{变量名}
 * 能解析类似#{user}或者#{user.id}或者{user.createBy.id}
 */
public class AnnotationResolver {

    private static AnnotationResolver resolver ;
	
	
    public static AnnotationResolver newInstance(){
		
        if (resolver == null) {
            return resolver = new AnnotationResolver();
        }else{
            return resolver;
        }
		
    }
	
	
    /**
     * 解析注解上的值
     * @param joinPoint
     * @param str 需要解析的字符串
     * @return
     */
    public Object resolver(JoinPoint joinPoint, String str) {

        if (str == null) return null ;
		
        Object value = null;
        if (str.matches("#\\{\\D*\\}")) {// 如果name匹配上了#{},则把内容当作变量
            String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", "");
            if (newStr.contains(".")) { // 复杂类型
                try {
                    value = complexResolver(joinPoint, newStr);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                value = simpleResolver(joinPoint, newStr);
            }
        } else { //非变量
            value = str;
        }
        return value;
    }

	
    private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        String[] strs = str.split("\\.");

        for (int i = 0; i < names.length; i++) {
            if (strs[0].equals(names[i])) {
                Object obj = args[i];
                Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);
                Object value = dmethod.invoke(args[i]);
                return getValue(value, 1, strs);
            }
        }

        return null;

    }

    private Object getValue(Object obj, int index, String[] strs) {

        try {
            if (obj != null && index < strs.length - 1) {
                Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);
                obj = method.invoke(obj);
                getValue(obj, index + 1, strs);
            }

            return obj;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private String getMethodName(String name) {
        return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
    }

	
    private Object simpleResolver(JoinPoint joinPoint, String str) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        for (int i = 0; i < names.length; i++) {
            if (str.equals(names[i])) {
                return args[i];
            }
        }
        return null;
    }
	
}

 

Published 47 original articles · won praise 16 · views 70000 +

Guess you like

Origin blog.csdn.net/zorro_jin/article/details/83687264