Springboot 自定义注解 AOP切面获取操作日志

1、application.yml加入声明

spring:  
  aop:  
    proxy-target-class: true  
    auto: true 

2.pom文件加入

	<dependency>
	  <groupId>org.springframework.boot</groupId>
	  <artifactId>spring-boot-starter-aop</artifactId>
	</dependency>

3.自定义注解@ControllerLog


 
 
package com.ps.uzkefu.util;

/**
 * Created by Administrator on 2018/6/22 0022.
 */

import java.lang.annotation.*;

/**
 * Created by zhangmy on 2017/7/24.
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ControllerLog {
    /**
     * 操作描述 业务名称business
     *
     * @return
     */
    String description() default "";

    /**
     * 操作模块
     *
     * @return
     */
    OperateModule module();

    /**
     * 操作类型 create modify delete
     *
     * @return
     */
    OperateType opType();

    /**
     * 主键入参参数名称,入参中的哪个参数为主键
     *
     * @return
     */
    String primaryKeyName() default "";

    /**
     * 主键在参数中的顺序,从0开始,默认0
     */
    int primaryKeySort() default 0;

    /**
     * 主键所属类
     *
     * @return
     */
    Class<?> primaryKeyBelongClass();

}

4. 定义模块名称枚举

 
 
 
 
package com.ps.uzkefu.util;

/**
 * Created by Administrator on 2018/6/22 0022.
 */
/**
 * Created by zhangmy on 2017/7/26.
 */
public enum OperateModule {
    SightMerchant("商家管理"), AdminUser("用户管理"), Tour("行程管理"), UserTour("用户行程");
    private String text;

    OperateModule(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}


5.定义操作类型枚举


package com.ps.uzkefu.util;

/**
 * Created by Administrator on 2018/6/22 0022.
 */



public enum OperateType {
    /**
     * 当操作类型为created时,要求方法返回格式为:return ok("具体操作信息", new MapBean("此处为实体主键属性名称", primaryKeyValue));
     */
    create, modify, delete
}

6. 切面

package com.ps.uzkefu.common;

/**
 * Created by Administrator on 2018/6/22 0022.
 */

import com.ps.uzkefu.util.ControllerLog;
import com.ps.uzkefu.util.OperateType;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zhangmy on 2017/7/25.
 */
@Aspect
@Component
public class WebLogAspect {


    private Logger logger = LoggerFactory.getLogger(this.getClass());


    /**
     * 获取@controllerLog 注解上信息
     *
     * @param joinPoint
     * @return map
     * @throws Exception
     */
    public static Map<String, Object> getControllerAnnotationValue(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        Map<String, Object> map = new HashMap<>();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] classes = method.getParameterTypes();
                if (classes.length == arguments.length) {
                    //取入参数据
                    String description = method.getAnnotation(ControllerLog.class).description();
                    String module = method.getAnnotation(ControllerLog.class).module().name();
                    String opType = method.getAnnotation(ControllerLog.class).opType().name();
                    String primaryKeyName = method.getAnnotation(ControllerLog.class).primaryKeyName();
                    int primaryKeySort = method.getAnnotation(ControllerLog.class).primaryKeySort();
                    Class<?> clazz = method.getAnnotation(ControllerLog.class).primaryKeyBelongClass();
                    map.put("module", module);
                    map.put("opType", opType);
                    map.put("business", description);
                    map.put("primaryKeyName", primaryKeyName);
                    map.put("primaryKeySort", primaryKeySort);
                    map.put("primaryKeyBelongClass", clazz);
                    break;
                }
            }
        }
        return map;
    }

    /**
     * 定义一个切入点.
     * ("execution(public * com.kfit.*.web..*.*(..))")
     * 解释下:
     * 第一个 * 代表任意修饰符及任意返回值.
     * 第二个 * 任意包名
     * 第三个 * 代表任意方法.
     * 第四个 * 定义在web包或者子包
     * 第五个 * 任意方法
     * .. 匹配任意数量的参数.
     */
    @Pointcut(" @annotation(com.ps.uzkefu.util.ControllerLog)")
    public void webLog() {
    }

    @Around("webLog()")
    public Object round(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("环绕通知开始........");
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        //入参  value
        Object[] args = joinPoint.getArgs();
        //入参名称
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        Map<String, Object> params = new HashMap<>();
        //获取所有参数对象
        for (int i = 0; i < args.length; i++) {
            if (null != args[i]) {
                if (args[i] instanceof BindingResult) {
                    params.put(paramNames[i], "bindingResult");
                } else {
                    params.put(paramNames[i], args[i]);
                }
            } else {
                params.put(paramNames[i], "无");
            }
        }
        Map<String, Object> values = getControllerAnnotationValue(joinPoint);
        String opType = values.get("opType").toString();
        String module = values.get("module").toString();
        String business = values.get("business").toString();
        String primaryKeyName = values.get("primaryKeyName").toString();
        int primaryKeySort = Integer.parseInt(values.get("primaryKeySort").toString());
        Class<?> primaryKeyBelongClass = (Class<?>) values.get("primaryKeyBelongClass");

        Object primaryKeyValue = null;
        if (StringUtils.isNotBlank(primaryKeyName) && OperateType.valueOf(opType) != OperateType.create) {
            primaryKeyValue = args[primaryKeySort];
        }
        //切面返回值
        // 一切正常的情况下,继续执行被拦截的方法
        // 一切正常的情况下,继续执行被拦截的方法
//        result = joinPoint.proceed();
        Object returnValue = joinPoint.proceed();
        if (OperateType.valueOf(opType) == OperateType.create) {
            // 主要目的是为了获取方法保存成功的返回数据格式,以获取保存成功的数据id
            // 此处要限制新增返回成功的格式,return ok("具体操作信息", new MapBean("此处为实体主键属性名称", primaryKeyValue));
            // 不然自己也可定义格式,进行拆分获取主键


        }

        return returnValue;
    }

    @AfterReturning("webLog()")
    public void doAfterReturning(JoinPoint joinPoint) {
        // 处理完请求,返回内容
        logger.info("WebLogAspect.doAfterReturning()");
    }

}
 
 

7.Cotroller中引用自定义注解,从而对该方法进行切面处理


package com.ps.uzkefu.apps;

import com.ps.uzkefu.apps.oms.user.entity.Menu;
import com.ps.uzkefu.base.BaseController;
import com.ps.uzkefu.util.ControllerLog;
import com.ps.uzkefu.util.OperateModule;
import com.ps.uzkefu.util.OperateType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

/**
 * Author:WuZhiWei
 * Date:2018/6/20
 */
@Controller
@RequestMapping
public class CorpIndexController extends BaseController {

    @RequestMapping("/")
    @ControllerLog(description = "修改商家状态", module = OperateModule.SightMerchant, opType = OperateType.modify, primaryKeyName = "id", primaryKeySort = 1, primaryKeyBelongClass = Menu.class)
    public String index(HttpServletRequest request) {
        return "/start/index";
    }

}


完整切面如下

package com.wdletu.log.aspect;


import com.wdletu.core.exception.ServiceException;
import com.wdletu.core.util.MapBean;
import com.wdletu.core.util.StringUtil;
import com.wdletu.log.annotation.ControllerLog;
import com.wdletu.log.constant.OperateType;
import com.wdletu.log.entity.OperateLog;
import com.wdletu.log.service.OperateLogService;
import com.wdletu.security.JwtService;
import com.wdletu.travel.admin.entity.AdminUser;
import com.wdletu.travel.admin.service.AdminUserService;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


import javax.persistence.Id;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;


/**
 * Created by zhangmy on 2017/7/25.
 */
@Aspect
@Component
public class WebLogAspect {
    @Autowired
    private OperateLogService operateLogService;
    @Autowired
    private JwtService jwtService;
    @Autowired
    private AdminUserService adminUserService;


    @Value("${jwt.header}")
    private String tokenHeader;


    private Logger logger = LoggerFactory.getLogger(this.getClass());


    /**
     * 获取class的主键字段名 主键值
     */
    public static Object getPrimaryKeyName(Class<?> clazz) throws Exception {
        Object param = null;
        //递归获取父子类所有的field
        Class tempClass = clazz;
        //当父类为null的时候说明到达了最上层的父类(Object类
        while (tempClass != null && !StringUtils.equals(tempClass.getName().toLowerCase(), "java.lang.object")) {
            Field[] fields = tempClass.getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                //boolean类型不必判断,因实体里包含boolean类型的属性,getter方法是以is开头,不是get开头
                if (field.getType().equals(Boolean.class) || field.getType().getName().equals("boolean")) {
                    continue;
                }
                if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
                    continue;
                }
                String getterMethod = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                Method method = tempClass.getDeclaredMethod(getterMethod);


                //字段上是否存在@Id注解
                Id primaryAnnotation = field.getAnnotation(Id.class);
                //getter方法上是否存在@Id注解
                if (primaryAnnotation == null) {
                    primaryAnnotation = method.getAnnotation(Id.class);
                }
                //存在@Id注解,则说明该字段为主键
                if (primaryAnnotation != null) {
                    /*String primaryKeyName = field.getName();*/
                    param = field.getName();
                    break;
                }
            }
            if (param != null && StringUtil.isNotBlank(param.toString())) {
                break;
            }
            //得到父类赋值给tempClass
            tempClass = tempClass.getSuperclass();
        }
        if (param == null) {
            throw new ServiceException(clazz.getName() + "实体,未设置主键");
        }
        return param;
    }


    /**
     * 获取@controllerLog 注解上信息
     *
     * @param joinPoint
     * @return map
     * @throws Exception
     */
    public static Map<String, Object> getControllerAnnotationValue(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        Map<String, Object> map = new HashMap<>();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] classes = method.getParameterTypes();
                if (classes.length == arguments.length) {
                    //取入参数据
                    String description = method.getAnnotation(ControllerLog.class).description();
                    String module = method.getAnnotation(ControllerLog.class).module().name();
                    String opType = method.getAnnotation(ControllerLog.class).opType().name();
                    String primaryKeyName = method.getAnnotation(ControllerLog.class).primaryKeyName();
                    int primaryKeySort = method.getAnnotation(ControllerLog.class).primaryKeySort();
                    Class<?> clazz = method.getAnnotation(ControllerLog.class).primaryKeyBelongClass();
                    map.put("module", module);
                    map.put("opType", opType);
                    map.put("business", description);
                    map.put("primaryKeyName", primaryKeyName);
                    map.put("primaryKeySort", primaryKeySort);
                    map.put("primaryKeyBelongClass", clazz);
                    break;
                }
            }
        }
        return map;
    }


    /**
     * 定义一个切入点.
     * ("execution(public * com.kfit.*.web..*.*(..))")
     * 解释下:
     * 第一个 * 代表任意修饰符及任意返回值.
     * 第二个 * 任意包名
     * 第三个 * 代表任意方法.
     * 第四个 * 定义在web包或者子包
     * 第五个 * 任意方法
     * .. 匹配任意数量的参数.
     */
    @Pointcut("execution(public * com.wdletu..*.controller..*.*(..))  && @annotation(com.wdletu.log.annotation.ControllerLog)")
    public void webLog() {
    }


    @Around("webLog()")
    public Object round(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("环绕通知开始........");
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        /*try {*/
        //读取用户
        String token = request.getHeader(tokenHeader);
        Long userId = jwtService.getUserIdFromToken(token);
        AdminUser adminUser = adminUserService.findByUserId(userId);
        String username = adminUser.getName();
        //入参  value
        Object[] args = joinPoint.getArgs();
        //入参名称
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        Map<String, Object> params = new HashMap<>();
        //获取所有参数对象
        for (int i = 0; i < args.length; i++) {
            if (null != args[i]) {
                if (args[i] instanceof BindingResult) {
                    params.put(paramNames[i], "bindingResult");
                } else {
                    params.put(paramNames[i], args[i]);
                }
            } else {
                params.put(paramNames[i], "无");
            }
        }
        Map<String, Object> values = getControllerAnnotationValue(joinPoint);
        String opType = values.get("opType").toString();
        String module = values.get("module").toString();
        String business = values.get("business").toString();
        String primaryKeyName = values.get("primaryKeyName").toString();
        int primaryKeySort = Integer.parseInt(values.get("primaryKeySort").toString());
        Class<?> primaryKeyBelongClass = (Class<?>) values.get("primaryKeyBelongClass");


        Object primaryKeyValue = null;
        if (StringUtils.isNotBlank(primaryKeyName) && OperateType.valueOf(opType) != OperateType.create) {
            primaryKeyValue = args[primaryKeySort];
        }
        //切面返回值
        Object returnValue = joinPoint.proceed();
        if (OperateType.valueOf(opType) == OperateType.create) {
            // 主要目的是为了获取方法保存成功的返回数据格式,以获取保存成功的数据id
            // 此处要限制新增返回成功的格式,return ok("具体操作信息", new MapBean("此处为实体主键属性名称", primaryKeyValue));
            // 不然自己也可定义格式,进行拆分获取主键
            primaryKeyName = getPrimaryKeyName(primaryKeyBelongClass).toString();
            if (returnValue instanceof ResponseEntity<?>) {
                Object entity = ((ResponseEntity<?>) returnValue).getBody();
                if (entity instanceof MapBean) {
                    MapBean mapBean = (MapBean) entity;
                    Boolean success = mapBean.get("success");
                    if (success) {
                        MapBean content = mapBean.get("content");
                        if (content != null) {
                            primaryKeyValue = content.get(primaryKeyName);
                        }
                    }
                }
            }
        }


        OperateLog operateLog = new OperateLog();
        operateLog.setParams(JSONObject.fromObject(params).toString());
        operateLog.setUserId(userId);
        operateLog.setUserName(username);
        operateLog.setModule(module);
        operateLog.setOpType(opType);
        operateLog.setBusiness(business);
        operateLog.setRecordId(primaryKeyValue == null ? null : Long.parseLong(primaryKeyValue.toString()));
        operateLogService.save(operateLog);
        /*} catch (Throwable throwable) {
            throwable.printStackTrace();
            logger.error("日志切面异常", throwable.getMessage());


        }*/
        return returnValue;
    }


    @AfterReturning("webLog()")
    public void doAfterReturning(JoinPoint joinPoint) {
        // 处理完请求,返回内容
        logger.info("WebLogAspect.doAfterReturning()");
    }


}

猜你喜欢

转载自blog.csdn.net/zsj777/article/details/80783307