バックエンド-「Javaカスタムアノテーションクラスは、ユーザー操作ログをデータベースに記録します

この記事を読んだ後に役立つかもしれないポイント:

1.ユーザー操作ログの記録方法。

2.Springのアスペクト指向プログラミングをより深く理解します。

3.カスタムアノテーションクラスの詳細をご覧ください。

具体的な実装手順は、おおよそ4つの手順です。

1.ロギングエンティティとデータテーブルを作成します。

2.カスタムアノテーションクラス。

3.アスペクトクラスを作成して、ログレコードの特定の操作ロジックを記述します

4.ビジネスメソッドにカスタムアノテーションを追加して、機能を実現します

エフェクト画像(Springboot + JPA + Layuiをベースに作成しました)

 具体的な実装は次のとおりです。

最初のステップは、次のようにエンティティを作成することです。

  

/**
 * 系统操作日志记录实体
 */
@Table(name = "sys_operation_log ")
@Entity
public class SysOperationLog {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /*数据创建时间*/
    private Date createTime;

    /*用户id*/
    private Long userId;

    /*用户昵称*/
    private String userName;

    /*操作类型*/
    @Enumerated(EnumType.STRING)
    private OperationType operationType;

    /*访问方法*/
    private String method;

    /*方法参数*/
    @Lob
    @Column(columnDefinition="text")
    private String params;

    /*方法简述*/
    private String operationDescribe;

    public Long getId() {
        return id;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public OperationType getOperationType() {
        return operationType;
    }

    public void setOperationType(OperationType operationType) {
        this.operationType = operationType;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getParams() {
        return params;
    }

    public void setParams(String params) {
        this.params = params;
    }


    public String getOperationDescribe() {
        return operationDescribe;
    }

    public void setOperationDescribe(String operationDescribe) {
        this.operationDescribe = operationDescribe;
    }
}
//与实体操作类型属性关联的枚举
public enum OperationType {
    /**
     * 操作类型
     */
    INSERT,//新增
    DELETE,//删除
    UPDATE,//更新
    EXPORT,//导出
    SELECT,//查询
    LOGON,//登录
    OTHER;//其他
}

2番目のステップ、カスタムアノテーション

/**
 * 自定义注解类
 */
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface Log {
    String describe() default "";//操作简述
    OperationType operationType() default OperationType.OTHER;//操作类型
}

3番目のステップはアスペクトクラスを作成することです。このデモのキーパッケージとパブリックガイドパッケージがリストされていることに注意してください。関連会社のパッケージパスはリストされていません。カスタマーオフィサーは自分で書く必要があります。ご容赦ください。

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.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 org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
import java.util.Date;


/**
 * 系统日志:切面处理类
 */
@Aspect
@Component
public class SysLogAspects {
    private static final Logger logger = LoggerFactory.getLogger(SysLogAspects.class);

    @Autowired
    private SysOperationLogJpaService sysOperationLogService;//这个service主要用来保存日志记录实体到数据库

    @Autowired
    private BaseController baseController;//系统公共controller

    @Autowired
    private UserJpaService userJpaService;//这个service主要用来保存和查询用户表


    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(com.baoji.log.service.Log)")
    public void logPoinCut() {
    }

     /*切面 配置通知,各注解作用
     *@Before:前置通知,在方法执行之前执行
     *@After:后置通知,在方法执行之后执行
     *@AfterRunning:返回通知,在方法返回结果之后执行,用这个注解参数会随方法改变,例新增一个实体,参数是一个id为null的,用这个注解后就会赋上真实的id
     *@AfterThrowing:异常通知,在方法抛出异常之后执行
     *@Around:环绕通知,围绕着方法执行
     * */
    @Before("logPoinCut()")
    public void saveSysLog(JoinPoint joinPoint) {
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        //操作简述
        String describe="";
        //操作类型的枚举
        OperationType operationType=OperationType.OTHER;
        Log log = method.getAnnotation(Log.class);
        if (log != null) {
            describe = log.describe()!=null? log.describe():"未知";
           operationType= log.operationType()!=null?log.operationType():OperationType.OTHER;
        }
        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        //请求的参数
        Object[] args = joinPoint.getArgs();
        Object[] arguments  = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
                continue;
            }
            arguments[i] = args[i];
        }
        //将参数所在的数组转换成json
        String params = "";
        if (arguments != null) {
            try {
                params = JSONObject.toJSONString(arguments);
            } catch (Exception e) {
                params = arguments.toString();
            }
        }
        int userid=0;
        String nickName="";
        try {
            /*用户还未登录*/
            if(operationType==OperationType.LOGON){
                /*获取将要登录的用户的用户名*/
                String userName=StringUtil.safeToString(arguments[0],"");
                /*根据用户名获取昵称*/
                nickName=userJpaService.getNickNameByName(userName);
                /*根据用户名获取用户id*/
                userid=userJpaService.getUserIdByName(userName);
                /*登录时参数包含用户名和密码等关键信息,为安全起见,不记录参数*/
                params="******";
            }else{
                /*获取系统当前登录用户的id*/
                userid= baseController.getLoginUserId();
                /*获取系统当前登录用户的昵称*/
                nickName=userJpaService.getUserNameById(userid);
            }
        } catch (OperationFailedException e) {
            logger.info("获取当前用户出错:{}", e.getMessage());
        }
        //保存日志
        SysOperationLog sysLog = new SysOperationLog();
        sysLog.setOperationDescribe(describe);
        sysLog.setMethod(className + "." + methodName);
        sysLog.setParams(params);
        sysLog.setUserId(new Long((long) userid));
        sysLog.setUserName(nickName);
        sysLog.setOperationType(operationType);
        sysLog.setCreateTime(new Date());
        logger.info("【操作记录】用户:{},在:{},进行了:{}操作,调用方法:{}",sysLog.getUserName(), DateUtil.formatDate(sysLog.getCreateTime()),sysLog.getOperationDescribe(),sysLog.getMethod());
        sysOperationLogService.save(sysLog);
    }
}

4番目のステップは、次のようにビジネス方法に関するコメントを追加することです。

@PutMapping("/psw")
@Log(describe = "修改自己密码",operationType= OperationType.UPDATE)
public String updatePsw(String oldPsw, String newPsw) 
        //具体业务逻辑
        return "修改失败";
}

 

おすすめ

転載: blog.csdn.net/nienianzhi1744/article/details/101058208