系统操作行为记录实战之方法添加注解+service层调用

简介

注: 开发工具使用的是IDEA

在通常的项目中,通常都需要一个用于对用户的操作进行记录的模块,主要负责记录各个用户对系统做个哪些操作;例如,张三用户成功登录系统,张三修改了个人资料,张三用户退出了系统等。对于这种全局的操作的记录,在实现代码中,常用以下两种方式:

- 调用Service层代码进行记录;
- 使用AOP思想添加注解。


1. 项目整体结构

操作行为记录项目结构图
类间关系图

2 代码详解

2.1 配置文件及实体类

2.1.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cmolong</groupId>
    <artifactId>behavior-logs</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>behavior-logs</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--切面AOP -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.1.2 application.yml
server:
  port: 9091
  servlet:
    context-path: /

2.1.3 Bean实体类–OperationLog

OperationLog 是一个po类,主要用来封装操作行为记录需要使用的字段。

package com.cmolong.behaviorlogs.bean;

/**
 * @program: behavior-logs
 * @description: 操作日志实体类
 * @author: 
 * @create: 
 */

public class OperationLog {

    // Id
    private String id;
    // 操作
    private String Operation;

    // 其他属性,通常情况下需要包括操作时间,操作人等信息。


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getOperation() {
        return Operation;
    }

    public void setOperation(String operation) {
        Operation = operation;
    }

    @Override
    public String toString() {
        return "OperationLog{" +
                "id='" + id + '\'' +
                ", Operation='" + Operation + '\'' +
                '}';
    }
}

2.1.4 工具类–ContextUtils
package com.cmolong.behaviorlogs.common.util;

import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @program: behavior-logs
 * @description: 工具类
 * @author: 
 * @create: 
 */
public class ContextUtils {

    public static HttpServletRequest getRequest() {
        return getRequestAttributes().getRequest();
    }

    public static HttpServletResponse getResponse() {
        return getRequestAttributes().getResponse();
    }

    public static HttpSession getSession() {
        return getRequest().getSession();
    }

    public static ServletRequestAttributes getRequestAttributes() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
    }

    public static ServletContext getServletContext() {
        WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
        return context == null ? null : context.getServletContext();
    }
}
2.1.5 启动类–BehaviorLogsApplication
package com.cmolong.behaviorlogs;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BehaviorLogsApplication {

    public static void main(String[] args) {
        SpringApplication.run(BehaviorLogsApplication.class, args);
    }
}

2.2 在方法上使用注解

2.2.1 自定义注解
package com.cmolong.behaviorlogs.annotation;


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

@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BehaviorAnno {

    String description() default "";
}

2.2.1 切面类
package com.cmolong.behaviorlogs.aspect;

import com.cmolong.behaviorlogs.annotation.BehaviorAnno;
import com.cmolong.behaviorlogs.bean.OperationLog;
import com.cmolong.behaviorlogs.common.util.ContextUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @program: behavior-logs
 * @description: 切面
 * @author: chengyantao
 * @create: 2021-05-14 15:12
 */
@Aspect
@Component
public class BehaviorDetailAspect {


    //@annotation是匹配拥有指定注解的方法
    @Pointcut("@annotation(com.cmolong.behaviorlogs.annotation.BehaviorAnno)")
    public void controllerAspect() {
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        // 此处在实际项目中常用,故先放在这,以备后用
        HttpServletRequest httpServletRequest = ContextUtils.getRequest();

        try {
            // 实际开发中,在此处添加操作日志,改写OperationLog实体类
            OperationLog operationLog = new OperationLog();
            operationLog.setId("UUID");
            // 获取描述
            operationLog.setOperation(getControllerMethodDescription(joinPoint));
            // 将操作日志进行保存入库,此处使用打印代替
            System.out.println("注解 : " + operationLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息
     * 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception 异常
     */
    private static String getControllerMethodDescription(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();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(BehaviorAnno.class).description();
                    break;
                }
            }
        }
        return description;
    }
}

2.3 service层调用

2.3.1 IBehaviorService 接口类
package com.cmolong.behaviorlogs.service;

/**
 * @program: behavior-logs
 * @author: 
 * @create: 
 */
public interface IBehaviorService {

    void saveBehaviorLog(String message);
}

2.3.2 BehaviorServiceImpl 类
package com.cmolong.behaviorlogs.service.impl;

import com.cmolong.behaviorlogs.bean.OperationLog;
import com.cmolong.behaviorlogs.common.util.ContextUtils;
import com.cmolong.behaviorlogs.service.IBehaviorService;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

/**
 * @program: behavior-logs
 * @description: 操作日志
 * @author: 
 * @create: 
 */
@Service
public class BehaviorServiceImpl implements IBehaviorService {


    /**
     * @param message 操作说明
     */
    @Override
    public void saveBehaviorLog(String message) {
        HttpServletRequest httpServletRequest = ContextUtils.getRequest();

        // 此处在实际项目中常用,故先放在这,以备后用
        OperationLog operationLog = new OperationLog();
        operationLog.setId("UUID");
        // 获取描述
        operationLog.setOperation(message);
        // 将操作日志进行保存入库,此处使用打印代替
        System.out.println("service层 : " + operationLog);
    }
}

2.4 indexController类
package com.cmolong.behaviorlogs.controller;

import com.cmolong.behaviorlogs.service.IBehaviorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cmolong.behaviorlogs.annotation.BehaviorAnno;
import com.cmolong.behaviorlogs.bean.OperationLog;

/**
 * @program: behavior-logs
 * @description: 登陆首页
 * @author: chengyantao
 * @create: 2021-05-14 15:32
 */
@RestController
@RequestMapping("api/")
public class indexController {

    @Autowired
    private IBehaviorService behaviorService;

    //以下两个方法实际开发中绝大多数都是分布在两个不同的controller中,此处只做展示使用。

    /**
     * 注解方式
     * @param operationLog
     * @return
     */
    @GetMapping("index")
    @BehaviorAnno(description = "#operationLog.Operation()")
    public String index(OperationLog operationLog){
        System.out.println("index..."); // 演示使用
        return "index";
    }

    /**
     * 调用service方式
     * @param operationLog
     * @return
     */
    @GetMapping("login")
    public String Login(OperationLog operationLog){
        System.out.println("Login...");  // 演示使用
        // do something
        behaviorService.saveBehaviorLog("用户XXXX登录系统。");
        return "welcome";
    }
}

2.5 测试

2.5.1 启动项目

启动项目

2.5.2 测试注解

浏览器(Postman)访问地址: http://localhost:9091/api/index

结果:
浏览器页面展示 index

系统console输出:
注解 : OperationLog{id=‘UUID’, Operation=’#operationLog.Operation()’}
index…

在这里插入图片描述

2.5.3 service 调用

浏览器(Postman)访问地址: http://localhost:9091/api/login

结果:
浏览器页面展示 welcome

系统console输出:
Login…
service层 : OperationLog{id=‘UUID’, Operation=‘用户XXXX登录系统。’}
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值