前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【七】springboot整合AOP实现日志操作

【七】springboot整合AOP实现日志操作

作者头像
小z666
发布2024-06-21 17:58:37
820
发布2024-06-21 17:58:37
举报
文章被收录于专栏:javajava

介绍:接下来我会把学习阶段学到的框架等知识点进行整合,每一次整合是在前一章的基础上进行的,所以后面的整合不会重复放前面的代码。每次的demo我放在结尾,本次是接着上一章的内容延续的,只增加新增的或者修改的代码。 上一章进行了Redis的整合,实现了一个用户对应一个token,用户登录失败次数锁定。 本章将整合AOP,实现请求接口后,对日志进行处理,不直接写到逻辑里面,尽量解耦,采用切面方式。

首先展示下我的目录结构:

勾选部分为本章整合后新增部分代码。

第一步:新增日志实体类

代码语言:javascript
复制
@Data
@TableName(value = "syslog")
@Accessors(chain = true)
public class SysLog {

    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.UUID)
    private String id;//id

    @TableField("operationUser")
    private String operationUser;//操作人

    @TableField("path")
    private String path;//请求路径

    @TableField("time")
    private String time;//方法执行时间

    @TableField("parameter")
    private String parameter;//方法入参

    @TableField("title")
    private String title;//操作方法

    @TableField("action")
    private String action;//方法描述

    @TableField("sysType")
    private Integer sysType;//系统类型

    @TableField("opType")
    private Integer opType;//操作类型

    public SysLog(String operationUser, String path, String time,
                      String parameter, String title, String action, Integer sysType, Integer opType) {
        super();
        this.operationUser = operationUser;
        this.path = path;
        this.time = time;
        this.parameter = parameter;
        this.title = title;
        this.action = action;
        this.sysType = sysType;
        this.opType = opType;
    }

}

ps:此处所涉及的注解请不要漏写,关系到mybatis-plus的使用,此处id的自动设置不一定使用 IdType.UUID。 可以采用前面整合的代码生成器进行生成,我在此处就是使用代码生成器生成的,但是需要改进一些地方,不然会报错。

第二步:新增各层代码

第三步:编写切面需要依赖的注解(次重点)

ps:自定义注解,需要加上框选部分的注解,里面参数的选择在csdn上面有大佬有细讲文章。

第四步:编写切面(重点)

代码语言:javascript
复制
@Aspect
@Component
@EnableAsync
public class SystemLogAspect {
    @Resource
    private SyslogMapper logMapper;//日志 mapper

    private String requestPath = null ; // 请求地址
    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private String user = null; // 操作人
    private HttpServletRequest request = null;//请求

    /**
     * 注解的位置
     */
    @Pointcut("@annotation(com.swagger.demo.config.OperationAnnotation)")
    public void logPointCut() {}

    /**
     * @param joinPoint
     * @Description 前置通知  方法调用前触发   记录开始时间,从session中获取操作人
     */
    @Before(value="logPointCut()")
    public void before(JoinPoint joinPoint){
        startTimeMillis = System.currentTimeMillis();
    }
    /**
     * @param joinPoint
     * @Description 获取入参方法参数
     * @return
     */
    public Map<String, Object> getNameAndValue(JoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            if(paramValues[i] instanceof Integer || paramValues[i] instanceof String) {
                param.put(paramNames[i], paramValues[i]);
            }
        }
        return param;
    }
    /**
     * @param joinPoint
     * @Description 后置通知    方法调用后触发   记录结束时间 ,操作人 ,入参等
     */
    @After(value="logPointCut()")
    public void after(JoinPoint joinPoint) {
        request = getHttpServletRequest();
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = null;
        try {
            targetClass = Class.forName(targetName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method[] methods = targetClass.getMethods();
        String title;
        String action;
        Integer sysType;
        Integer opType;
        Class<?>[] clazzs;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                clazzs = method.getParameterTypes();
                if (clazzs!=null&&clazzs.length == arguments.length
                        &&method.getAnnotation(OperationAnnotation.class)!=null) {
                    request = getHttpServletRequest();
                    requestPath=request.getServletPath();
                    HttpSession session = request.getSession();
                    user = session.getAttribute("userName").toString();
                    title = method.getAnnotation(OperationAnnotation.class).content();
                    action = method.getAnnotation(OperationAnnotation.class).action();
                    sysType = method.getAnnotation(OperationAnnotation.class).sysType();
                    opType = method.getAnnotation(OperationAnnotation.class).opType();
                    endTimeMillis = System.currentTimeMillis();

                    SysLog log=new SysLog(user, requestPath,
                            (endTimeMillis-startTimeMillis)+"ms",
                            getNameAndValue(joinPoint).toString(), title, action,sysType,opType);
                    System.out.println("增加参数:"+log);
                    logMapper.insert(log);
//                    break;
                }
            }
        }
    }
    /**
     * @Description: 获取request
     */
    public HttpServletRequest getHttpServletRequest(){
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        return request;
    }
    /**
     * @param joinPoint
     * @return 环绕通知
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        return null;
    }
    /**
     * @param joinPoint
     * @Description 异常通知
     */
    public void throwing(JoinPoint joinPoint) {
        System.out.println("异常通知");
    }
}

ps:下面对该切面进行一个解释。

aspect注解表示将该类定义为切面。component注解表示将该类注册到spring容器。

pointcut注解表示定义切入点,此处表示,切入到 OperationAnnotation注解的位置,定义切入点的参数特别多,可以去细细了解一下。

before注解表示前置通知,相当于BeforeAdvice的功能,value的值为切入点的名字。

此方法是获取方法的入参。

after注解表示后置通知,即使切入点注解切入位置执行完之后执行该部分代码。此处从session里面获取当前用户,表示日志的操作人(在登录时将用户名存入session),实际使用也可以采用其他办法。

使用mybatis-plus的insert方法直接新增的记录,也可以自己写sql。

补充:改造了一下登录接口,登录成功后报错了用户名到session,如下。

ps:使用session需要在入参处加上HttpServletRequest request。

从登录的controller方法到实现类都需要加上该入参。

第五步:演示

请求登录接口

结果:

已经新增成功。

本期整合到此完毕,接下来会继续更新加强整合,尽情期待。

访问地址:http://localhost:8088/swagger-ui.html或者http://localhost:8088/doc.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一步:新增日志实体类
  • 第二步:新增各层代码
  • 第三步:编写切面需要依赖的注解(次重点)
  • 第四步:编写切面(重点)
  • 第五步:演示
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档