欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot通過自定義注解與異步來管理日志流程

 更新時間:2023年03月27日 09:26:33   作者:☆夜幕星河℡  
實現(xiàn)日志管理說實話方式還挺多,個人使用過直接在Controller代碼里面寫、AOP+自定義注解、ConstraintValidator。本文主要和大家講的是自定義注解與異步來管理日志流程,感興趣的可以了解一下

一、前言

我們在企業(yè)級的開發(fā)中,必不可少的是對日志的記錄,實現(xiàn)有很多種方式,常見的就是基于AOP+注解進行保存,同時考慮到程序的流暢和效率,我們可以使用異步進行保存!

二、基礎(chǔ)環(huán)境

1. 導(dǎo)入依賴

我這里的springboot版本是:2.7.4

<?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.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-log</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-log</name>
    <description>springboot-log 日志 Demo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.16</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2. 編寫yml配置

server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    druid:
      initial-size: 5
      max-active: 100
      min-idle: 5
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 30000
      validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false

三、數(shù)據(jù)庫設(shè)計

數(shù)據(jù)庫保存日志表的設(shè)計,這里一切從簡,一般日志多的后期會進行分庫分表,或者搭配ELK進行分析,分庫分表一般采用根據(jù)方法類型!

DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志主鍵',
  `title` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '模塊標(biāo)題',
  `business_type` int(2) NULL DEFAULT 0 COMMENT '業(yè)務(wù)類型(0其它 1新增 2修改 3刪除)',
  `method` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '方法名稱',
  `request_method` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '請求方式',
  `oper_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '操作人員',
  `oper_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '請求URL',
  `oper_ip` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '主機地址',
  `oper_time` datetime(0) NULL DEFAULT NULL COMMENT '操作時間',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1585197503834284034 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '操作日志記錄' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

實體類:

package com.example.springbootlog.entity;
import java.util.Date;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.ToString;
/**
 * 操作日志記錄(SysLog)實體類
 *
 * @author qrxm
 * @since 2023-03-26 02:09:54
 */
@Data
@ToString
@TableName("sys_log")
public class SysLog implements Serializable {
    private static final long serialVersionUID = 811241510868515068L;
    /**
    * 日志主鍵
    */
    @TableId
    private Long id;
    /**
    * 模塊標(biāo)題
    */
    private String title;
    /**
    * 業(yè)務(wù)類型(0其它 1新增 2修改 3刪除)
    */
    private Integer businessType;
    /**
    * 方法名稱
    */
    private String method;
    /**
    * 請求方式
    */
    private String requestMethod;
    /**
    * 操作人員
    */
    private String operName;
    /**
    * 請求URL
    */
    private String operUrl;
    /**
    * 主機地址
    */
    private String operIp;
    /**
    * 操作時間
    */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date operTime;
}

四、主要功能

大體思路:

  • 先手寫一個注解
  • 切面來進行獲取要保存的數(shù)據(jù)
  • 一個發(fā)布者來發(fā)布要保存的數(shù)據(jù)
  • 一個監(jiān)聽者監(jiān)聽后保存(異步)

完整項目架構(gòu)圖如下:

1. 編寫注解

package com.example.springbootlog.annotation;
import com.example.springbootlog.constant.BusinessTypeEnum;
import java.lang.annotation.*;
/**
 * 自定義操作日志記錄注解
 * @author qrxm
 */
@Target(ElementType.METHOD) // 注解只能用于方法
@Retention(RetentionPolicy.RUNTIME) // 修飾注解的生命周期
@Documented
public @interface Log {
    String value() default "";
    /**
     * 模塊
     */
    String title() default "測試模塊";
    /**
     * 方法名稱
     */
    String method() default "測試方法";
    /**
     * 功能
     */
    BusinessTypeEnum businessType() default BusinessTypeEnum.OTHER;
}

2. 業(yè)務(wù)類型枚舉

package com.example.springbootlog.constant;
public enum BusinessTypeEnum {
    /**
     * 其它
     */
    OTHER(0,"其它"),
    /**
     * 新增
     */
    INSERT(1,"新增"),
    /**
     * 修改
     */
    UPDATE(2,"修改"),
    /**
     * 刪除
     */
    DELETE(3,"刪除");
    private Integer code;
    private String message;
    BusinessTypeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    public Integer getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

3. 編寫切片

這里是以切片后進行發(fā)起的,當(dāng)然規(guī)范流程是要加異常后的切片,這里以最簡單的進行測試哈,大家按需進行添加??!

package com.example.springbootlog.aspect;
import com.example.springbootlog.annotation.Log;
import com.example.springbootlog.entity.SysLog;
import com.example.springbootlog.listener.EventPubListener;
import com.example.springbootlog.utils.IpUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
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.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Aspect
@Component
public class SysLogAspect {
    private final Logger logger = LoggerFactory.getLogger(SysLogAspect.class);
    @Autowired
    private EventPubListener eventPubListener;
    /**
     * 以注解所標(biāo)注的方法作為切入點
     */
    @Pointcut("@annotation(com.example.springbootlog.annotation.Log)")
    public void sysLog() {}
    /**
     * 在切點之后織入
     * @throws Throwable
     */
    @After("sysLog()")
    public void doAfter(JoinPoint joinPoint) {
        Log log = ((MethodSignature) joinPoint.getSignature()).getMethod()
                .getAnnotation(Log.class);
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String method = request.getMethod();
        String url = request.getRequestURL().toString();
        String ip = IpUtils.getIpAddr(request);
        SysLog sysLog = new SysLog();
        sysLog.setBusinessType(log.businessType().getCode());
        sysLog.setTitle(log.title());
        sysLog.setMethod(log.method());
        sysLog.setRequestMethod(method);
        sysLog.setOperIp(ip);
        sysLog.setOperUrl(url);
        // 從登錄中token獲取登錄人員信息即可
        sysLog.setOperName("我是測試人員");
        sysLog.setOperTime(new Date());
        // 發(fā)布消息
        eventPubListener.pushListener(sysLog);
        logger.info("=======日志發(fā)送成功,內(nèi)容:{}",sysLog);
    }
}

4. ip工具類

package com.example.springbootlog.utils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import javax.servlet.http.HttpServletRequest;
public class IpUtils {
    /**
     * 獲取客戶端IP
     *
     * @param request 請求對象
     * @return IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
    }
    /**
     * 從多級反向代理中獲得第一個非unknown IP地址
     *
     * @param ip 獲得的IP地址
     * @return 第一個非unknown IP地址
     */
    public static String getMultistageReverseProxyIp(String ip) {
        // 多級反向代理檢測
        if (ip != null && ip.indexOf(",") > 0) {
            final String[] ips = ip.trim().split(",");
            for (String subIp : ips) {
                if (false == isUnknown(subIp)) {
                    ip = subIp;
                    break;
                }
            }
        }
        return ip;
    }
    /**
     * 檢測給定字符串是否為未知,多用于檢測HTTP請求相關(guān)
     *
     * @param checkString 被檢測的字符串
     * @return 是否未知
     */
    public static boolean isUnknown(String checkString) {
        return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
    }
}

5. 事件發(fā)布

事件發(fā)布是由ApplicationContext對象進行發(fā)布的,直接注入使用即可!

使用觀察者模式的目的:為了業(yè)務(wù)邏輯之間的解耦,提高可擴展性。

這種模式在spring和springboot底層是經(jīng)常出現(xiàn)的,大家可以去看看。

發(fā)布者只需要關(guān)注發(fā)布消息,監(jiān)聽者只需要監(jiān)聽自己需要的,不管誰發(fā)的,符合自己監(jiān)聽條件即可。

package com.example.springbootlog.listener;
import com.example.springbootlog.entity.SysLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class EventPubListener {
    @Autowired
    private ApplicationContext applicationContext;
    /**
     * 事件發(fā)布方法
     * @param sysLogEvent
     */
    public void pushListener(SysLog sysLogEvent) {
        applicationContext.publishEvent(sysLogEvent);
    }
}

6. 監(jiān)聽者

@Async:單獨開啟一個新線程去保存,提高效率!

@EventListener:監(jiān)聽

package com.example.springbootlog.listener;
import com.example.springbootlog.entity.SysLog;
import com.example.springbootlog.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class MyEventListener {
    @Autowired
    private SysLogService sysLogService;
    // 開啟異步
    @Async
    // 開啟監(jiān)聽
    @EventListener(SysLog.class)
    public void saveSysLog(SysLog event) {
        log.info("=====即將異步保存到數(shù)據(jù)庫======");
        sysLogService.saveLog(event);
    }
}

五、測試

1. controller

package com.example.springbootlog.controller;
import com.example.springbootlog.annotation.Log;
import com.example.springbootlog.constant.BusinessTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
 * 操作日志記錄(SysLog)表控制層
 *
 */
@Slf4j
@RestController
@RequestMapping("sysLog")
public class SysLogController {
    @Log(method = "測試添加方法", title = "測試呢", businessType = BusinessTypeEnum.INSERT)
    @GetMapping("/saveLog")
    public void saveLog() {
        log.info("我就是來測試一下是否成功!");
    }
}

2. service

package com.example.springbootlog.service;
import com.example.springbootlog.entity.SysLog;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * 操作日志記錄(SysLog)表服務(wù)接口
 */
public interface SysLogService  extends IService<SysLog> {
    Integer saveLog(SysLog sysLog);
}
package com.example.springbootlog.service.impl;
import com.example.springbootlog.entity.SysLog;
import com.example.springbootlog.service.SysLogService;
import com.example.springbootlog.dao.SysLogDao;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 操作日志記錄(SysLog)表服務(wù)實現(xiàn)類
 */
@Service("sysLogService")
public class SysLogServiceImpl  extends ServiceImpl<SysLogDao, SysLog>  implements SysLogService {
    @Autowired
    private SysLogDao sysLogDao;
    @Override
    public Integer saveLog(SysLog sysLog) {
        return sysLogDao.insert(sysLog);
    }
}

3. dao

這里使用mybatis-plus進行保存

package com.example.springbootlog.dao;
import com.example.springbootlog.entity.SysLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * 操作日志記錄(SysLog)表數(shù)據(jù)庫訪問層
 */
public interface SysLogDao extends BaseMapper<SysLog> {
}

4. 測試

訪問地址:http://localhost:8080/sysLog/saveLog

5. 數(shù)據(jù)庫

六、總結(jié)

這個實戰(zhàn)在企業(yè)級必不可少的,每個項目搭建人不同,但是結(jié)果都是一樣的,保存日志到數(shù)據(jù),這樣可以進行按鈕的點擊進行統(tǒng)計,分析那個功能是否經(jīng)常使用,那些東西需要優(yōu)化。只要是有數(shù)據(jù)的東西,分析一下總會有收獲的!后面日志多了就行分庫分表,ELK搭建。

代碼已上傳到 Gitee 上面了,地址:https://gitee.com/afoams/springboot-log

到此這篇關(guān)于SpringBoot通過自定義注解與異步來管理日志流程的文章就介紹到這了,更多相關(guān)SpringBoot自定義注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java求s=a+aa+aaa+aaaa+aa...a 5個數(shù)相加的值

    Java求s=a+aa+aaa+aaaa+aa...a 5個數(shù)相加的值

    求s=a+aa+aaa+aaaa+aa...a的值,其中a是一個數(shù)字。例如2+22+222+2222+22222(此時共有5個數(shù)相加),幾個數(shù)相加有鍵盤控制
    2017-02-02
  • spring mvc url匹配禁用后綴訪問操作

    spring mvc url匹配禁用后綴訪問操作

    這篇文章主要介紹了spring mvc url匹配禁用后綴訪問操作,具有很好的參考價值,希望對大家有所幫助。以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家
    2021-07-07
  • 詳解Java同步—線程鎖和條件對象

    詳解Java同步—線程鎖和條件對象

    在這篇文章中給大家詳細講述了Java同步—線程鎖和條件對象的相關(guān)知識點,有需要的讀者們可以參考下。
    2018-07-07
  • 海量數(shù)據(jù)去重排序bitmap(位圖法)在java中實現(xiàn)的兩種方法

    海量數(shù)據(jù)去重排序bitmap(位圖法)在java中實現(xiàn)的兩種方法

    今天小編就為大家分享一篇關(guān)于海量數(shù)據(jù)去重排序bitmap(位圖法)在java中實現(xiàn)的兩種方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • Android中幾種圖片特效的處理的實現(xiàn)方法

    Android中幾種圖片特效的處理的實現(xiàn)方法

    這篇文章主要介紹了 Android中幾種圖片特效的處理的實現(xiàn)方法的相關(guān)資料,這里有放大縮小圖片,獲得圓角圖片,獲得帶倒影圖片的幾種方法,需要的朋友可以參考下
    2017-08-08
  • JAVA8 List<List<Integer>> list中再裝一個list轉(zhuǎn)成一個list操作

    JAVA8 List<List<Integer>> list中再裝一個list轉(zhuǎn)成一個list操

    這篇文章主要介紹了JAVA8 List<List<Integer>> list中再裝一個list轉(zhuǎn)成一個list操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 帶你快速搞定java數(shù)組

    帶你快速搞定java數(shù)組

    數(shù)組是相同類型數(shù)據(jù)的有序集合數(shù)組描述的是相同類型的若干個數(shù)據(jù),按照一定的先后次序排列組合而成。其中,每一個數(shù)據(jù)稱作一個數(shù)組元素,每個數(shù)組元素可以通過一個下標(biāo)來訪問它們數(shù)組的聲明創(chuàng)建
    2021-07-07
  • SpringBoot中的@DependsOn注解詳解

    SpringBoot中的@DependsOn注解詳解

    這篇文章主要介紹了SpringBoot中的@DependsOn注解詳解,
    2023-08-08
  • 一文詳解SpringMVC中的@RequestMapping注解

    一文詳解SpringMVC中的@RequestMapping注解

    @RequestMapping是一個用于映射HTTP請求到處理方法的注解,在Spring框架中使用,它可以用于控制器類和處理方法上,用來指定處理不同URL路徑的請求,并定義請求的方法等,本文小編將給大家詳細的介紹一下SpringMVC中的@RequestMapping注解,需要的朋友可以參考下
    2023-08-08
  • Spring根據(jù)XML配置文件注入屬性的方法

    Spring根據(jù)XML配置文件注入屬性的方法

    下面小編就為大家?guī)硪黄猄pring根據(jù)XML配置文件注入屬性的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11

最新評論