springBoot,AOP切面实现日志记录,自定义注解,注解属性动态传参

SpringBoot,AOP切面实现日志记录,自定义注解,注解属性动态传参

项目需求:
需要记录用户的操作具体的方法,记录当前具体操作的是哪一条数据信息,作为参数也一并保存起来。
如:更新数据,IP,电脑号,调用方法,类型为修改,及这条更新的数据具体信息。

用SpringAOP + Annotation来实现

  • 自定义aop注解类,通用日志文件
import java.lang.annotation.*;

/******************************
 * 用途说明: 通用日志文件
 * 作者姓名: wangqiubo
 * 创建时间: 2021/03/24
 ******************************/
@Target(ElementType.METHOD)         //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented                         //生成文档
public @interface MyLog {
    /**
     * 业务类型,例如:"新增"
     */
    String type() default "";

    /**
     * 业务的操作,例如:"用户信息: 新增XXX用户"
     */
    String name() default "";

}
  • controller方法上,加入我们自定义的MyLog注解
    /**
     * 分页查询
     */
    @PostMapping("/{pageNum}/{pageSize}")
    @ApiOperation(value = "operateLog-ofPage", notes = "分页查询操作日志列表")
     /**
     * 注意:就是下面这一行
     * @MyLog 是自定义注解, type,name 是我们在自定义注解里面的
     * type本文是引用的常量,name是前端参过来的实体类
     * 当然type,name也可以写死 如:
     * @MyLog(type = "查询", name = "查询所有数据")
     */ 
    @MyLog(type = ConstantList.SELECT, name = "#{sysOperateLogDto.operateType}")
    //-----------------------上面这一行--------------------------------------
    public CallResult ofPage(@ApiParam(value = "页号", required = true) @PathVariable("pageNum") int pageNum,
                             @ApiParam(value = "页大小", required = true) @PathVariable("pageSize") int pageSize,
                             @ApiParam(value = "条件", required = true) @RequestBody SysOperateLogDto sysOperateLogDto) {
        log.info("分页查询操作日志参数:pageNum: "+ pageNum + "  pageSize: " + pageSize + "  sysOperateLogDto: " +sysOperateLogDto);

        QueryWrapper<SysOperateLog> queryWrapper = new QueryWrapper();
        queryWrapper.eq(!StringUtils.isEmpty(sysOperateLogDto.getOperatorId()),"operator_id",sysOperateLogDto.getOperatorId())
                    .eq(!StringUtils.isEmpty(sysOperateLogDto.getOperateType()),"operate_type",sysOperateLogDto.getOperateType())
                    .ge(!StringUtils.isEmpty(sysOperateLogDto.getStartDate()),"operate_time",sysOperateLogDto.getStartDate())
                    .le(!StringUtils.isEmpty(sysOperateLogDto.getEndDate()),"operate_time",sysOperateLogDto.getEndDate());
        queryWrapper.orderByDesc("operate_time");

        IPage<SysOperateLog> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        page = operateLogService.page(page, queryWrapper);
        log.info("返回操作日志列表:page: "+ page.getRecords().toString());
        return CallResult.success("操作日志列表", page);
    }
  • 定义springAOP切面,来获取动态参数,及调用方法的信息

import com.cgnpc.psc.manage.controller.SysOperateLogController;
import com.cgnpc.psc.manage.domain.SysOperateLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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 javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/******************************
 * 用途说明: 日志AOP切入
 * 作者姓名: wangqiubo
 * 创建时间: 2021/03/24
 ******************************/
@Aspect
@Component
public class LogAspect {
    @Autowired
    SysOperateLogController sysOperateLogController;   

    //定义切点                     定义切入点MyLog(扫描切入点路径)
    @Pointcut(value ="@annotation(com.cgnpc.psc.manage.config.sysPperateLog.MyLog)")
    public void logPointCut() {
    }

    //定义切面 日志切面
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();      //获取RequestAttributes
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);                         //从RequestAttributes中获取HttpServletRequest的信息

        System.out.println("==============================begin==============================");
        Object result = point.proceed();                                        //去执行被访问的切面方法
        MethodSignature signature = (MethodSignature) point.getSignature();     //从切面织入点处通过反射机制获取织入点处的方法
        Method method = signature.getMethod();                                  //获取切入点所在的方法
//        String methodName = method.getName();                                   //获取方法名
//        System.out.println("Method Name:" + methodName);                        //输出方法名
//        Map<String, String> rtnMap = converMap(request.getParameterMap());      //请求的参数
//        String className = point.getTarget().getClass().getName();              //获取请求的类名

 		/**
         * 获取动态参数值
         */
        MyLog log = method.getAnnotation(MyLog.class);
        System.out.println("type:" + log.name() + "name:" + log.name());        //输出注解里面的值
        Object type = AnnotationResolver.newInstance().resolver(point, log.type());
        Object name = AnnotationResolver.newInstance().resolver(point, log.name());

        InetAddress addr = InetAddress.getLocalHost();                          //获取主机信息
        System.out.println("Local HostAddress: "+addr.getHostAddress());        //本地主机ip地址
        String hostname = addr.getHostName();                                   //本地主机名称
        System.out.println("Local host name: "+hostname);
        System.out.println("++++++++++++++++++++++++++++++end++++++++++++++++++++++++++++++");

        //定义操作日志实体类
        SysOperateLog sysOperateLog = new SysOperateLog(); 
        sysOperateLog.setOperateType(type.toString());              //操作类型
        sysOperateLog.setMessage("操作信息:" + name.toString());     //操作信息
        sysOperateLog.setOperateTime(new Date());                   //操作时间
        sysOperateLog.setHostName(addr.getHostAddress());           //主机名(ip)
 
        //调用操作日志新增方法
        sysOperateLogController.addOperateLog(sysOperateLog);
        return result;
    }

    /**
     * 转换request 请求参数
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

}
  • AnnotationResolver 是解析注解类语法的解析器,可以把注解类中的语法直接解析#{方法变量名}
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

/**
 * 该类的作用可以把方法上的参数绑定到注解的变量中,注解的语法#{变量名}
 * 能解析类似 "#{sysOperateLogDto.operateType}"
 */
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;
    }

}
  • ConstantList 常量列表,供AOP切面使用,操作日志调用

/**
 * 常量列表,供AOP切面使用,操作日志调用
 */
public class ConstantList {

    public static final String SELECT = "查询" ;

    public static final String ADD = "新增" ;

    public static final String UPDATE = "修改" ;

    public static final String DELETE = "删除" ;

    public static final String BATCH_DELETE = "批量删除" ;

}

到这里就全结束了,希望对你有所帮助!!!

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值