天天看點

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

介紹:接下來我會把學習階段學到的架構等知識點進行整合,每一次整合是在前一章的基礎上進行的,是以後面的整合不會重複放前面的代碼。每次的demo我放在結尾,本次是接着上一章的内容延續的,隻增加新增的或者修改的代碼。

上一章進行了Redis的整合,實作了一個使用者對應一個token,使用者登入失敗次數鎖定。

本章将整合AOP,實作請求接口後,對日志進行處理,不直接寫到邏輯裡面,盡量解耦,采用切面方式。 

首先展示下我的目錄結構: 

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

勾選部分為本章整合後新增部分代碼。

第一步:新增日志實體類

@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。

可以采用前面整合的代碼生成器進行生成,我在此處就是使用代碼生成器生成的,但是需要改進一些地方,不然會報錯。

第二步:新增各層代碼

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝
【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝
【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

第三步:編寫切面需要依賴的注解(次重點)

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

ps:自定義注解,需要加上框選部分的注解,裡面參數的選擇在csdn上面有大佬有細講文章。

第四步:編寫切面(重點)

@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:下面對該切面進行一個解釋。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

aspect注解表示将該類定義為切面。

component注解表示将該類注冊到spring容器。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

pointcut注解表示定義切入點,此處表示,切入到 OperationAnnotation注解的位置,定義切入點的參數特别多,可以去細細了解一下。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

 before注解表示前置通知,相當于BeforeAdvice的功能,value的值為切入點的名字。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

 此方法是擷取方法的入參。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

after注解表示後置通知,即使切入點注解切入位置執行完之後執行該部分代碼。此處從session裡面擷取目前使用者,表示日志的操作人(在登入時将使用者名存入session),實際使用也可以采用其他辦法。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

使用mybatis-plus的insert方法直接新增的記錄,也可以自己寫sql。 

補充:改造了一下登入接口,登入成功後報錯了使用者名到session,如下:

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

ps:使用session需要在入參處加上HttpServletRequest request。

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

從登入的controller方法到實作類都需要加上該入參。

第五步:示範

請求登入接口
【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

結果:

【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝

已經新增成功。

本期整合到此完畢,接下來會繼續更新加強整合,盡情期待。

通路位址:http://localhost:8088/swagger-ui.html或者http://localhost:8088/doc.html

demo位址:https://github.com/zrc11/studydemo/tree/main/%E6%95%B4%E5%90%88swagger

碼字不易,若幫到各位,幫忙三連,感謝